数学建模社区-数学中国

标题: 【Java演示】什么是链表?数据结构 [打印本页]

作者: 杨利霞    时间: 2020-5-4 15:55
标题: 【Java演示】什么是链表?数据结构
4 E1 B% g; d! ?8 L: a$ }
【Java演示】什么是链表?数据结构
7 C; ]" ~: C3 F& p4 J. i) C7 b一、单向链表
2 B9 I: w& M/ u5 o$ ]! f3 @5 _* o$ Y* U+ h: [
1.png
* S# b( Z: U9 f8 k! y5 Q! s链表(linked list)是一种在物理上非连续、非顺序的数据结构,由若干节点(node)所组成。
3 N9 z" K- l5 S. A1 ?" W) y单向 链表的每一个节点又包含两部分,一部分是存放数据的变量data,另一部分是指向下一个节点的指针next。3 d7 }; ^& E! s# |: V6 P
链表的第1个节点被称为头节点,最后1个节点被称为尾节点,尾节点的next指针指向空。
2 \, [9 M4 H9 K! ?; Q7 V2 y
5 Z& [$ F5 X; _. V7 S什么叫随机存储呢?5 p3 N2 v; U( _9 E9 G. c
( u, [. ^8 o$ A. a. q$ e8 F# \5 x' R
如果说数组在内存中的存储方式是顺序存储,那么链表在内存中的存储方式则是随机存储 。
2 M# {/ c5 I: J1 o8 R9 b" o4 N上一节我们讲解了数组的内存分配方式,数组在内存中占用了连续完整的存储空间。而链表则采用了见缝插针的方式,链表的每一个节点分布在内存的不同位置,依靠next指针关联起来。这样可以灵活有效地利用零散的碎片空间。( s9 G$ i5 w: W
3.png 6 G6 W$ k* E% g6 p: ?
9 h$ @- b; p9 J2 i/ ]2 h% p

图中的箭头代表链表节点的next指针。

链表的基本操作1. 查找节点

在查找元素时,链表不像数组那样可以通过下标快速进行定位,只能从头节点开始向后一个一个节点逐一查找。

4.png 6 T1 t& O6 d" K

2 c4 [& \1 r" h9 H: K/**& P8 |5 `& v3 M7 Q
     * 链表查找元素
  q& i* J4 F! X     *
* G0 }5 [2 G4 u/ Y     * @param index 查找的位置* o% r" W! ]4 F/ O! Z( b- W
     * @return index位置的Node对象; {8 g  i! h# h  X$ w; F
     */
0 Q# X8 y% m" a    public Node get(int index) {/ y, W: [8 B' |, K' A0 w* Y
        if (index < 0 || index > size) {" F8 N4 F/ y, K6 o/ R' l) L' N4 P0 X
            throw new IndexOutOfBoundsException("超出链表的节点的范围!");
7 R" R$ l0 M( U% L$ l5 _        }4 Z( G7 p/ E3 q& q/ m, O
        Node temp = head;
+ R2 W! F# j. K% N8 ^0 D- C" `        for (int i = 0; i < index; i++) {
3 }5 S5 I+ N* a% J" o            temp = temp.next;
# Y' i2 l1 N) l. |* m        }3 B9 T# D4 h4 [& M. ^
        return temp;% ~+ ?9 H$ U: n+ x0 Q0 Q
    }
# e6 B9 e7 u0 z! L4 K" z' q5 Q
- C4 A0 \0 x. s1 k$ X  h

链表中的数据只能按顺序进行访问,最坏的时间复杂度是O(n)

2. 更新节点 5.png 8 }; B: h# u$ S

7 K7 b% g6 X9 O  e如果不考虑查找节点的过程,链表的更新过程会像数组那样简单,直接把旧数据替换成新数据即可。$ L6 P$ w2 k6 u+ a  F/ [8 v
如果不考虑查找元素的过程,只考虑纯粹的更新节点操作,时间复杂度是O(1)
  @; ]1 d6 Y) O1 u" K6 P6 D/**
  f2 p$ l. Y& J# P6 d- I     * 更新节点 将列表中指定位置的节点的data替换为指定的data。
( K$ r& u' @0 [% f9 v     *
0 {' e& U, s: o; k: w: g     * @param index 需要更新的节点的位置. q; u- \$ o6 I4 B7 A  v
     * @param data  新data
/ |& Z  g4 {4 |* |% b; G     * @return 旧data
8 ~) D- H1 {  f     */3 }- p9 ^* I8 J4 c& E9 @
    public int set(int index, int data) {, }& u7 N. R& V% _
        Node x = get(index);
! w" X6 i) \! m+ m* }        int oldVal = x.data;3 n: W" j. @' e" W4 c6 p( V- }
        x.data = data;1 ^' v8 F! R& `. H" _. [
        return oldVal;
4 b4 w6 H6 q, X1 b( t    }  U. ~' Y. k" ~# n# J
  R* f( q' H( f0 i: y! Z4 T
3. 插入节点

只要内存空间允许,能够插入链表的元素是无穷无尽的,不需要像数组那样考虑扩容的问题。

与数组类似,链表插入节点时,同样分为3种情况。

3.1. 尾部插入

尾部插入,是最简单的情况,把最后一个节点的next指针指向新插入的节点即可。

2 i1 g4 {2 A7 n7 E' e2 H; {
6.png
# X3 e2 x/ T7 B3.2. 头部插入

头部插入,可以分成两个步骤。

7.png
" w& V! D" `0 g; ]1 r# Q" n* T  @+ X" J
3.3. 中间插入

中间插入,同样分为两个步骤。

8.png ( y5 h  L- Q  T: x- j

( v: g# [. a. m7 S: G! {三钟情况的代码合到一起
5 z; t+ Y7 P( P
/ }3 s& q. o5 Q( y( l+ i8 Y7 U! V/**
3 e& g' k5 p6 K4 z6 x  G     * 链表插入元素
8 s- ]/ L3 e' b8 h     *
- w; b' R6 H! q/ c, n     * @param index 插入位置
4 r7 Z. @# ?- J     * @param data  插入元素 被插入的链表节点的数据
9 @4 B6 Q$ ?# v* T     */4 }- w/ _& [$ ~# H3 ]( F  ?
    public void insert(int index, int data) {& X- U) r; N) S/ x
        if (index < 0 || index > size) {% [! y* @9 B3 t( A1 R& O% f2 ?
            throw new IndexOutOfBoundsException("超出链表节点范围!");
1 r6 v4 T9 x$ k4 ]/ f        }
8 e% r2 U" O. }1 o: ?  z6 Q- X, Q$ {        Node insertedNode = new Node(data);
0 s0 L4 ~  W/ I! {2 ]. `        if (size == 0) {  @5 p, ]7 B: J0 K) ^% G! |
            //空链表& a, ]. D+ [) E9 b. M5 k
            head = insertedNode;- U" s8 s7 A9 U
            last = insertedNode;
/ W2 E- m3 I, h& I2 f4 E1 D        } else if (index == 0) {
; [& K& i. s4 J7 J* ?            //插入头部" N! X9 F0 e) R# e
            insertedNode.next = head;
" l: L5 f4 K; D' T            head = insertedNode;
/ B9 e% x' T* m3 X/ H        } else if (size == index) {5 i) ~3 l5 o( X. ^: E5 W
            //插入尾部
2 b5 d, z8 Z, E5 t. j1 S            last.next = insertedNode;& w4 P) S2 Y3 ^  b9 J+ Y- X8 `
            last = insertedNode;$ D+ d: j6 N% z6 D2 r
        } else {
7 Z$ @- b3 r/ h            //插入中间
+ o% o# {; c/ X+ l4 P) u. i! g( Q1 G            Node prvNode = get(index - 1);
4 ^- {/ u. Y7 q% i9 |7 `& u3 a            insertedNode.next = prvNode.next;" V8 s* Y) Q0 Y- C* M1 c
            prvNode.next = insertedNode;
* d% q: k2 K/ Y  v- X3 l        }
. ]' ^+ N- E3 k/ u) m8 N9 C; l        size++;3 k% q6 ]5 U+ m
    }7 y8 r& ^4 X) A
  F5 }( R* `5 i  ^2 h
/**
( ?' F4 c  ^" x" U) s9 n* o     * 链表插入元素, S) d9 T" f* J* j
     *
$ d" \" f. o1 I  U1 i$ w, G     * @param index 插入位置
1 e& l* V' h' `" ~     * @param data  插入元素 被插入的链表节点的数据
; }, D+ n7 ^1 ~( H1 J  D     */
5 ~8 p/ Y+ k8 i0 Q4 u/ ~    public void insert(int index, int data) {
* r+ S" Q! h. ~        if (index < 0 || index > size) {' f, K2 d$ I* X5 Q/ s
            throw new IndexOutOfBoundsException("超出链表节点范围!");
& F! M! x- Z! F/ y  `: F        }1 `. R  l. \1 f5 T
        Node insertedNode = new Node(data);' g: e' {' L& e
        if (size == 0) {2 k; U/ Y0 r3 q2 k, ~9 m
            //空链表# G7 `# f% \- F3 J( M7 C
            head = insertedNode;
8 D7 E* c# D' n' y            last = insertedNode;
! i+ h  i- c2 Z7 n        } else if (index == 0) {
0 l7 U' J: Q& T' M7 {            //插入头部; q0 j, s, U7 d3 c
            insertedNode.next = head;; ?- [& u, A' J: W$ ]
            head = insertedNode;
3 }. }9 A& u: I' b+ a2 u. E" Q7 q; |        } else if (size == index) {, R4 P: k& F% o+ i: U
            //插入尾部4 j6 x, E( u& \! F, m
            last.next = insertedNode;. _/ r9 L: b: W& U( O4 K
            last = insertedNode;
; e/ V' X. V/ C2 U$ \6 p9 O        } else {
; }/ o9 `  x' ^! x0 ~$ t$ B" J            //插入中间
# X6 W; B; r% o/ v            Node prvNode = get(index - 1);
6 Y/ e# I( k* k6 ^% t3 D4 P7 m            insertedNode.next = prvNode.next;' N* w' s5 N- R* e# x
            prvNode.next = insertedNode;' M# g) g( c/ E; v
        }6 W7 J" K, D: E3 _
        size++;. M9 B8 s. T3 N9 }/ s
    }- l0 F9 ^7 k- d* B6 _7 `( ]
4. 删除元素

链表的删除操作同样分为3种情况。

4.1. 尾部删除

尾部删除,是最简单的情况,把倒数第2个节点的next指针指向空即
" _; n  [) d* B$ ?6 D# O9 J% y可。

9.png 6 f3 Z- ~) E& {" T: r! I5 o
0 H" W" R% U. l$ G
4.1. 头部删除

头部删除,也很简单,把链表的头节点设为原先头节点的next指针即可。

10.png
( \4 t1 h% B  b* a0 v, I' u* b! S/ y8 e
4.1. 中间删除

中间删除,同样很简单,把要删除节点的前置节点的next指针,指向要$ Z' I% b3 c4 a
删除元素的下一个节点即可。

11.png
6 ~) s- |; f* f1 }' a. D4 P: G
4 M4 X5 ^4 W4 L' e! I" o
. Z% W, A" r0 ~% r, E* w这里需要注意的是,许多高级语言,如Java,拥有自动化的垃圾回收机制,所以我们不用刻意去释放被删除的节点,只要没有外部引用指向它们,被删除的节点会被自动回收。
% `2 R9 n3 M' [5 M1 J如果不考虑插入、删除操作之前查找元素的过程,只考虑纯粹的插入和删除操作,时间复杂度都是O(1)9 ^* T5 U2 Q6 h4 Y. v. x% m4 P; `
/**
! [* d  D5 _1 y# o: Y' p. r# c6 I     * 链表删除元素
! C% }6 l8 {- d+ s1 u     *
; ~. F. ]- ?/ k6 y- b' Z6 J     * @param index 删除的位置" o+ n0 @; c7 Q- m6 M2 f, p5 @0 J
     * @return 被删除的节点
5 \6 k# O& X5 `4 Q' U* r  U     */8 q+ n+ R' a4 M9 P) _3 y" |5 q0 `
    public Node remove(int index) {2 S/ z; M7 g0 W% f
        if (index < 0 || index > size) {
. m, T; l3 b) A) u  O3 Z            throw new IndexOutOfBoundsException("超出链表节点范围");0 ?5 E3 _  K# Q5 J1 ?
        }
9 U* R8 [+ M0 w5 g7 x/ |4 A6 ^1 p        Node removeNode;6 m' \+ l$ U! d6 {* _  {
        if (index == 0) {" Z% B2 w2 M  f  ~) m$ Z5 L
            if (size == 0) {
5 {4 |1 _, V5 F2 p, c  N, C- k                throw new NullPointerException("当前链表为空,不可以进行删除操作");( B7 {' e! L7 I) a0 t
            }. b5 M# Z% o! C! O; m5 E! K) U
            //删除头节点
1 O$ I1 J6 v& Y            removeNode = head;
9 a5 K+ q$ m$ ?. W( Y( \$ u            head = head.next;6 `, O( ]; i, B! c4 C0 j
        } else if (index == size - 1) {
0 m/ b4 n1 w9 k2 v7 F! ?- S/ h            //删除尾节点
9 b9 _9 L1 O2 r# n! A" e0 s7 w            Node preNode = get(index - 1);
6 o+ b9 e# E% ^/ W, ^. p            removeNode = preNode.next;
- M4 ^7 p. `% ^3 y. V; C            preNode.next = null;! H, I. l, }/ ~
            last = preNode;
& ?% U5 u3 U; t1 P3 }% O        } else {
8 k% w. l% a9 h            //删除中间节点
! J0 K/ B  e7 x) E4 |            Node prevNode = get(index - 1);$ m' a7 v# O, k* o& Y
            removeNode = prevNode.next;
! y5 Q9 V5 r% @% r0 z( o            prevNode.next = prevNode.next.next;
& B0 Z2 k. j7 _) V( M# q1 x3 V        }
$ V6 I5 q/ V1 T        size--;
- `5 |/ V' }( X' u# p        return removeNode;( K0 K( J8 R. Q6 q
    }! }" a7 {& {4 g# E* `1 z# p
Java实现链表的完整代码package chapter2.part2;
% d& \2 q9 G# M3 }4 D2 w7 k6 g: ]' j2 z3 A+ t% f
/**
" Z( l2 z. p: L* Q' { * Created by IntelliJ IDEA.
$ x. ^, J9 z2 H" e: J *) z1 D6 [2 ?* u- p! g! C
* @Author: 张志浩  Zhang Zhihao
* O( N, r- a& Q( U7 x7 e * @Email: 3382885270@qq.com3 u6 x) C8 U( }
* @Date: 2020/5/3
+ z! d  z+ k5 P7 u4 X6 g1 v * @Time: 13:39
0 M0 _. \3 \0 M * @Version: 1.0
3 S; P. [* ?% q */
9 L* O7 z, \& Q$ i& y* b7 y2 qpublic class MyLinkedList2 {. k7 n* e, A" r7 L0 T3 J: G+ ]; O- f
    private Node head; //头节点
/ V/ m" [; K2 N$ k9 \5 O    private Node last; //尾节点
5 M8 {7 w* N) U' Q# J1 j    private int size; //链表实际长度
  V3 ?/ C2 N4 W8 x  }' g1 _4 u
) X; K7 B8 ~/ _* s! T' W    public static void main(String[] args) {
3 ]& }# H$ ]1 V6 _! F' r        MyLinkedList2 myLinkedList = new MyLinkedList2();" p2 u8 f, d* t: a) K
//        myLinkedList.remove(0); // java.lang.NullPointerException: 当前链表为空,不可以进行删除操作3 G  A; R2 ^- J% D3 Z+ H) y
//        myLinkedList.remove(3); // java.lang.IndexOutOfBoundsException: 超出链表节点范围' T- w1 B; a; G! }
        myLinkedList.insert(0, 3);' d$ d* v9 `0 ^+ i& \: d: f
        myLinkedList.insert(1, 7);
7 X! x  n' G6 U$ \- y$ V* H        myLinkedList.insert(2, 9);# \% z! ]7 r1 v& d4 w' C
        myLinkedList.insert(3, 5);  j3 a) C3 K+ n) U. `
        myLinkedList.insert(1, 6);
9 s2 \( W1 ?" K        myLinkedList.remove(0);" u. t" A" Q; `& O
        myLinkedList.set(0, 23);( H* x5 V& n, ]) U! \- f
        myLinkedList.output();
) p4 Q' f. k/ ?" k6 o; U+ W* k    }
+ S$ W: }% B/ K4 x: q4 _0 J* J, l4 H! n+ W0 l$ Q
    /**! |$ C7 h0 P* Y' h6 ^
     * 链表插入元素
1 ^: Q& l& _' I" q1 j2 r  H0 }     *
" H, G. R9 T7 B     * @param index 插入位置( R2 y' d7 E  e) a3 v) i& C
     * @param data  插入元素 被插入的链表节点的数据9 Q* T- a1 A1 z" ^( l
     */+ z/ p9 D: {7 Q+ w6 N3 H0 p
    public void insert(int index, int data) {
) ?9 `- f2 P0 c  R& H        if (index < 0 || index > size) {$ ^0 R( t" K# R/ h( c: \
            throw new IndexOutOfBoundsException("超出链表节点范围!");
; |8 P# G  Z5 q, y, N8 `5 c2 @        }  ~' _& E5 W# T9 k2 L
        Node insertedNode = new Node(data);
# [4 c0 v: o  V* J( u: Q, V. b        if (size == 0) {
% Z" f9 B5 I- m3 R) @            //空链表
; R, O6 |" L; j; I+ j            head = insertedNode;
/ V2 @+ y9 R2 i" y7 p            last = insertedNode;
; _* j) R( k/ U& [" r        } else if (index == 0) {
3 L2 j; D2 a! [5 u            //插入头部
- a/ K7 L0 I7 `            insertedNode.next = head;/ o$ m4 Z0 b7 A8 }
            head = insertedNode;
9 Z7 {2 M# K: M+ C6 n        } else if (size == index) {
- r* M( `9 t3 P, U) }) b* J            //插入尾部3 G1 h; N$ e/ `- H3 M4 R5 z
            last.next = insertedNode;
9 R/ G/ K4 W1 U0 u            last = insertedNode;
% r: u. L$ ]3 [7 q7 L        } else {
* {  T3 ?# j9 c+ n  c# u3 h            //插入中间
; {6 y2 ?. h9 j# e: S) q            Node prvNode = get(index - 1);& r  }+ A7 o. v& c7 H
            insertedNode.next = prvNode.next;: i" ^: M# |4 ^) l- Z$ t" D2 W3 z
            prvNode.next = insertedNode;5 ^$ D0 k, ?; [8 ]
        }& x4 N8 m6 h% E' k
        size++;
, K! ^; o+ z7 _    }9 O; Q4 R! z. z0 l  M5 M" o) z

' k0 O4 j7 H4 V+ T    /**6 K! C1 M0 ?' `: d3 U) E
     * 链表删除元素
& H& {. T; b% q; Y     *
* k$ h, A. ^: \% \# b5 R# v3 X6 T     * @param index 删除的位置
8 ^7 I4 @% h9 d' @3 D# u     * @return 被删除的节点, F8 Y5 l  ]) r% E/ Z
     */# K. ~$ b  b+ j% C/ U
    public Node remove(int index) {
* t) N  B; S# \* _6 P        if (index < 0 || index > size) {$ Y8 k& g3 g' \5 |; k
            throw new IndexOutOfBoundsException("超出链表节点范围");' Q6 a0 }+ ~) x/ c- p/ u0 i3 h
        }, R2 I& A3 I& V. ?1 C0 e( Y/ ?
        Node removeNode;
$ k' a; r* E1 ]) u5 r. |        if (index == 0) {) x6 L9 p; R% d( }8 A
            if (size == 0) {
4 U7 _' Y$ v0 S                throw new NullPointerException("当前链表为空,不可以进行删除操作");
2 ~, s  c9 r4 [! M' M3 w4 g            }
* B6 y, E* n5 j& t4 p& b1 G            //删除头节点
4 d, o1 E; c" v; n- ?4 i            removeNode = head;, F. U2 v- H9 D9 y
            head = head.next;
  t- W9 R& x$ S3 z! S9 b+ _' h        } else if (index == size - 1) {) G; j0 Z6 p$ Z! m9 {; J3 {. X3 m
            //删除尾节点
% P8 w4 k# }& ~8 i& b# q            Node preNode = get(index - 1);
5 r2 N. M+ a  Y4 b( p" Y1 b            removeNode = preNode.next;
" \6 Y! A7 [/ L! Q+ m            preNode.next = null;
: G3 L* ]' ?8 H8 n. s            last = preNode;
/ K/ w: g, s7 t- B* Y        } else {! ]* q7 s3 v4 {* ~$ i0 X
            //删除中间节点" y8 `5 B; w) c# U$ S% q7 p6 U8 [
            Node prevNode = get(index - 1);& }) e2 u  ?4 c! Y( c3 {
            removeNode = prevNode.next;
! e' R% }3 D) Q7 L            prevNode.next = prevNode.next.next;9 W6 q! n$ e# P$ i6 p" }, T
        }' W' I# c/ A1 K3 V: v; v
        size--;2 m2 C3 ?5 K& [1 h% _* q! P
        return removeNode;
& W& u& N; P' O6 o    }$ p) x" b' j$ |
) Z0 ]% m7 V5 q' C6 J5 Z
    /**
( F" `& l9 e( f3 C" _     * 更新节点 将列表中指定位置的节点的data替换为指定的data。) O! K( U! a2 _8 h. X
     *
$ E4 _6 S: \2 @7 {, x4 Q     * @param index 需要更新的节点的位置! ~( y' ?. B6 a* |
     * @param data  新data3 b# t' ~$ P. s% `0 r7 q3 _
     * @return 旧data3 V; @1 h( [/ \6 e
     */4 p# Y" d2 z, K0 B
    public int set(int index, int data) {: ]; Q& p8 s3 i
        Node x = get(index);
  t5 h$ U4 q6 @        int oldVal = x.data;) {& F: k) Z) e2 W3 _
        x.data = data;
3 ?, G6 v" ~4 R, w2 v        return oldVal;
" I8 j2 E1 s- o3 W. W; s) @    }% }- s% ~) O3 R

2 E* c6 n; k6 K$ X3 \  f, d    /**+ b6 b( w3 s1 c; A; o
     * 链表查找元素
4 Q/ T% O8 F5 d8 m% H     *- r: I# b* L+ O8 E0 m- Q
     * @param index 查找的位置1 f  t$ c# b: Z
     * @return index位置的Node对象
9 `: y8 M  ~# W7 ?, ^! N     */
$ r2 l- ?( ]5 |6 M% D: Z$ ?    public Node get(int index) {
) v0 M. k9 t0 X9 z        if (index < 0 || index > size) {
+ ~* o$ I3 C5 K- \2 ?) m$ E            throw new IndexOutOfBoundsException("超出链表的节点的范围!");7 h5 k7 n# c  l/ h! _
        }. f7 Z( Q0 l5 _( ~9 f: {
        Node temp = head;( {+ _1 n) H- f: w
        for (int i = 0; i < index; i++) {. ^  H7 g! r. I1 C5 A0 f
            temp = temp.next;) U3 s* K% J3 t( K2 ]2 ?& a
        }" C3 W) q3 Q4 V) c
        return temp;# Z) E+ k* i6 D" t
    }6 J3 A% `; _7 e% D

5 u* E0 `4 Z2 f5 ^0 P: T/ ~& u/ r    /**! a7 r9 I4 a2 s4 x4 Q& u
     * 输出链表6 F5 Y, R, G/ W( Q6 M; S
     */# ]0 d5 O# }% K! e6 |" B
    public void output() {  ~7 g* p  H8 a8 i
        Node temp = head;
0 m+ E* t  S4 R& g! N3 b        while (temp != null) {
1 G/ E* T/ Z) @            System.out.print(temp.data + " ");
0 o/ C$ d- h8 m; H            temp = temp.next;
0 b1 E  A) o; w( X        }
! U* Y7 P+ a) @2 U. q! n8 s    }
3 m3 B" `) C. b0 k) z* t0 ^( v) M; a  q
    /**
. ~' k8 ?( t3 I     * 链表节点) i  G  y% Q+ W7 i# I
     */% [7 t  ^5 f; l* P5 G
    class Node {) K5 M+ [- Q# U. D
        int data;1 O0 \/ h2 O0 k% ~- t! K& c, Z
        Node next;. C, W. E$ {3 G. U( j2 U) }* K- y
: }3 |6 F7 f  Q* D
        Node(int data) {( H- K' ?! b2 |
            this.data = data;7 J( Z0 b" @5 s5 _
        }
1 W6 L. e$ U! r) _# |    }
2 A1 C0 |4 M# A+ V' ?}; N% W/ G2 E- `- a$ W1 j

3 }% E3 z6 g6 A0 d2 }! B# I0 _0 o
+ o2 e  E7 R, j% E; V二、双向链表 12.png . S3 |) Y$ @! ]6 K
双向链表比单向链表稍微复杂一些,它的每一个节点除了拥有data和next指针,还拥有指向前置节点的prev 指针。$ N6 g1 }2 }. \( l( g" F

0 o2 |( @" J: |; G6 O1 L# D1 t+ L, x7 V0 O6 |- T/ V

2 N( C2 V# q9 S# \" u5 v2 m5 D. k! V0 N7 F" b$ ?9 i* u
————————————————  [1 L3 ?  |( X! C) n6 u
版权声明:本文为CSDN博主「爱做梦的鱼」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。1 S: j( V$ r5 w
原文链接:https://blog.csdn.net/weixin_43124279/article/details/105904468
' Q7 G* x3 t2 b2 n' ^1 H
; y( {$ T9 f; U9 I! f' X/ s
! O/ {" [& `9 E8 v9 Q6 ^0 x

2.png (256.36 KB, 下载次数: 272)

2.png






欢迎光临 数学建模社区-数学中国 (http://www.madio.net/) Powered by Discuz! X2.5