8 P$ @+ K& ]) r- l2 \0 a【Java演示】什么是链表?数据结构; W+ M9 \* |9 G
一、单向链表
] {( l; s8 L' R3 A8 I
0 I- A% o) `& C, i/ b, y, h
; m! m: m) ?7 g$ M: @ T8 a% s
链表(linked list)是一种在物理上非连续、非顺序的数据结构,由若干节点(node)所组成。7 Y4 Z: t8 e- N7 c) o$ B; f+ u S0 c
单向 链表的每一个节点又包含两部分,一部分是存放数据的变量data,另一部分是指向下一个节点的指针next。
' I- Q% T5 c9 x5 ]8 t3 w: C链表的第1个节点被称为头节点,最后1个节点被称为尾节点,尾节点的next指针指向空。% M, b% r! u0 w6 e4 m/ N
B/ F9 Q: A( p; R+ t Y' S
什么叫随机存储呢?, { F% B# H3 [. X! x
0 n2 N) s# I, j% n0 c5 J
如果说数组在内存中的存储方式是顺序存储,那么链表在内存中的存储方式则是随机存储 。
1 ^; j. O. d, |3 T' v8 m5 F- U上一节我们讲解了数组的内存分配方式,数组在内存中占用了连续完整的存储空间。而链表则采用了见缝插针的方式,链表的每一个节点分布在内存的不同位置,依靠next指针关联起来。这样可以灵活有效地利用零散的碎片空间。+ y7 g# b V& }& v N% E4 \
5 C. d9 z8 A% f9 ^" f( ~' _
8 D$ Y- e( [. y9 I4 E6 Z$ w8 ~图中的箭头代表链表节点的next指针。 链表的基本操作1. 查找节点在查找元素时,链表不像数组那样可以通过下标快速进行定位,只能从头节点开始向后一个一个节点逐一查找。
, P6 T6 |* Y3 b7 y+ ], ]9 r) q' n2 ^" X4 S9 h1 A5 i
/**, t& o1 Y" {* Y7 X. H, x: M7 |3 w# x
* 链表查找元素* ]2 ^, K |5 V
*' N, o3 _: t( I( q
* @param index 查找的位置( F( S: g1 p' s/ ~* k
* @return index位置的Node对象
; t* C2 a7 w. r b3 y r0 w */8 {) o; Q ?& `/ g: b. F
public Node get(int index) {- r3 y1 U; @# b u, E( Q9 \8 g
if (index < 0 || index > size) {( _( _/ \ n1 Q, Y7 J) \' }
throw new IndexOutOfBoundsException("超出链表的节点的范围!");2 E3 K5 X8 o% I
}
) m; H( T1 J' w Node temp = head;( L4 \# D1 j9 H1 x
for (int i = 0; i < index; i++) {: }: W) r% a+ J- D2 o1 v
temp = temp.next;* L* C& ]3 ~7 J) d! l" J+ p4 }, j
}! p3 ?% k, d/ }' ^1 G# I
return temp;
8 E+ L6 }& M9 }. j }
( V8 u N+ K& l1 X4 h6 n9 u( U/ s8 c' ~ y
链表中的数据只能按顺序进行访问,最坏的时间复杂度是O(n) 2. 更新节点
3 i$ w7 ] i" c& j" @% ?3 F5 u4 R
9 v* @0 d. o" H& |8 v5 C6 Q0 z
如果不考虑查找节点的过程,链表的更新过程会像数组那样简单,直接把旧数据替换成新数据即可。! {7 M3 W2 s d" y6 l( O: |
如果不考虑查找元素的过程,只考虑纯粹的更新节点操作,时间复杂度是O(1)* I, U) t8 A$ A k+ \+ S/ p g
/**
. v7 a* g5 v2 `6 r k. A5 f * 更新节点 将列表中指定位置的节点的data替换为指定的data。
% _, J2 I& s9 \2 c+ K+ t6 f/ Z7 c *
/ p' n& w0 y1 L2 J! P * @param index 需要更新的节点的位置
: S7 f J$ |$ W2 C9 v+ t * @param data 新data
! z: _: s# S: c8 S; _' z * @return 旧data" e4 e2 v- w7 {& G: X; `0 m
*/, D$ e3 z% r9 e* K
public int set(int index, int data) {
4 \% Y9 G5 _5 |( }8 p' O8 d6 ?. x Node x = get(index);
5 f; c$ n0 w; z$ Z6 R% ^+ a$ R# Z& U int oldVal = x.data;! n& s- G. o% n
x.data = data;
6 M( V" E l8 _# V$ ]; e return oldVal;6 K: @2 g7 u; c6 P/ D" h
}
6 S6 F( a- y3 [- }) p9 c1 Z# }
* S7 u! R- ^ q8 {& K4 c7 A3. 插入节点只要内存空间允许,能够插入链表的元素是无穷无尽的,不需要像数组那样考虑扩容的问题。 与数组类似,链表插入节点时,同样分为3种情况。 - 尾部插入
- 头部插入
- 中间插入
. v* Z2 v9 v3 n9 M 3.1. 尾部插入尾部插入,是最简单的情况,把最后一个节点的next指针指向新插入的节点即可。 % }1 I! j7 z8 h% G' _2 ?4 e, G
$ S- h5 D( E, ]. T2 l/ R
3.2. 头部插入头部插入,可以分成两个步骤。 - 第1步,把新节点的next指针指向原先的头节点。
- 第2步,把新节点变为链表的头节点。
5 N5 I6 x- m# N/ @1 W
5 k: ^! `, D: v+ d1 M7 R3 @' G- ~) X
0 O; _/ [6 U( c% G. p, Q, F3.3. 中间插入中间插入,同样分为两个步骤。 - 第1步,新节点的next指针,指向插入位置的节点。
- 第2步,插入位置前置节点的next指针,指向新节点。4 B5 [3 H$ u. U: W% c, {
0 V/ l) `$ p2 T* M3 w# z% @" ]$ j& _. n( }
三钟情况的代码合到一起
7 _6 P0 O( t0 u- F+ N9 `6 a( s
$ s) T# \9 a" @- d4 k% C/**
! U' C. I/ x$ C3 ~% N, x * 链表插入元素
) [& h2 f! {1 t4 _; U6 F *2 f3 t# v& r* t* u; k7 ^
* @param index 插入位置' O. ^& E% x- l( a' S! V/ M
* @param data 插入元素 被插入的链表节点的数据# c' y: m" {, B
*/
, m+ |) e' L% C% {6 l- M1 U) s0 U& L public void insert(int index, int data) {$ G6 l2 W, l/ ~! ?3 q, [9 |! f
if (index < 0 || index > size) {
4 X* k6 t+ \* J3 T/ N* S& C8 T throw new IndexOutOfBoundsException("超出链表节点范围!");5 o1 V/ n# `$ E
}0 K; p" k# L8 s( t" Q
Node insertedNode = new Node(data);
5 b9 G9 _$ Y- ?! E' j+ f9 F if (size == 0) {
# y) } K0 }8 u, t* ^ //空链表
# Y0 S: E* r, U head = insertedNode;
; z. U5 p+ B; k: W last = insertedNode;/ k0 c4 V8 f) L
} else if (index == 0) {
9 o% P& W( Z# a) j T3 L //插入头部
0 \) V1 t* r2 \) w insertedNode.next = head;, s! e, b3 w, s# s" @" V5 r
head = insertedNode;
, M/ ~( h' D8 @ } else if (size == index) {
7 c/ l' ?* E8 Z& O //插入尾部$ V$ u+ w3 O7 U% t6 T. m5 d
last.next = insertedNode;3 C$ ? a2 w' P1 F& V, Q( _
last = insertedNode;8 }1 M. N4 l) r
} else { N8 D+ q h1 |1 Q5 f( k! s4 {9 z
//插入中间
5 M# q/ B4 x) n# m$ | Node prvNode = get(index - 1);
9 f% Y( z4 E' q0 {7 Z insertedNode.next = prvNode.next;' v1 @) Y0 b4 E5 Q) ^# }! t
prvNode.next = insertedNode;( Z, b: w1 m$ @. a `: _
}
- j& _2 ]! q6 I3 `# D( [" D' Q- Y size++;' q5 {+ b4 A- b# Q2 ?
}: H6 Z0 f" F4 S2 j( d' h; w
! \& c2 A/ e$ O! ^( k7 G2 ]/**
8 S7 A! \( ]+ p- ] * 链表插入元素
) }! _4 v0 V( p, B- D *) F$ @' e; @/ G9 @% W7 l
* @param index 插入位置
" |) x1 F4 e. T& i5 m * @param data 插入元素 被插入的链表节点的数据
) m) x$ R( E, C' S6 G */
0 v8 C" T4 ]$ t8 J- X& f: ` public void insert(int index, int data) {. ?9 X" I2 o+ ] A
if (index < 0 || index > size) {3 d3 M! A2 r' q! Q; r9 N8 J( m
throw new IndexOutOfBoundsException("超出链表节点范围!");
/ Q5 I3 h8 I+ y- L, I, \ }3 L& m w, s' p. u1 Z. v" _
Node insertedNode = new Node(data);
. u5 x$ V$ T& Q. v5 e8 y8 b if (size == 0) {
7 r' ~$ Y2 @( v //空链表- `6 ^# u/ b2 t, m6 w, H
head = insertedNode;7 E5 o" C: F. j% A5 V5 I
last = insertedNode;: B" P% F# P2 r$ n/ D# b
} else if (index == 0) {, p& B. v" D; a. q0 l/ ~
//插入头部7 B* J3 i, I7 i9 [- F- [( X
insertedNode.next = head;0 U# n( d4 K. U/ ?, p' m6 o
head = insertedNode;7 l) }5 ] x! o" \' x8 L; O
} else if (size == index) {
# ~5 E1 |" }1 `; b //插入尾部8 F3 U- Y5 f) g7 c3 o
last.next = insertedNode;9 ]' S4 c o" y
last = insertedNode; c( s% w* t" i3 g, \, h
} else {' a) O5 r$ f5 l) w" c
//插入中间3 [+ u, p/ S- z2 s q2 N! V E1 ~7 v
Node prvNode = get(index - 1);/ @' [& g9 M8 G5 m
insertedNode.next = prvNode.next;
/ I. Z# d/ r5 }5 t1 N$ Q9 l9 e) f0 \ prvNode.next = insertedNode;
~% ~/ u2 R/ |# V }. \! w. b+ a$ i9 P
size++;
3 N3 T. W. h g$ W8 m( s% O4 l6 U }) k/ d* }( S# R; n+ X% A8 P
4. 删除元素链表的删除操作同样分为3种情况。 - 尾部删除
- 头部删除
- 中间删除
: c( {- I! J- X2 H 4.1. 尾部删除尾部删除,是最简单的情况,把倒数第2个节点的next指针指向空即: Y5 ]+ r% C7 H* w
可。
& q/ z& O1 v _3 D
2 j0 ?, h: A. N5 Z! ^9 f6 g4.1. 头部删除头部删除,也很简单,把链表的头节点设为原先头节点的next指针即可。
- u; q6 d( o5 L# N9 ~+ G2 B
9 T6 {$ p" n" w" ?% z4.1. 中间删除中间删除,同样很简单,把要删除节点的前置节点的next指针,指向要
; |! G' j; B) T' Y8 t$ p7 e: e删除元素的下一个节点即可。
2 \- |" y1 x5 d8 C, ]2 | d7 _* J- r$ I# z7 ]6 ?/ i' \+ ]
z5 v3 z* f$ V) q3 i1 `( x" M& C
这里需要注意的是,许多高级语言,如Java,拥有自动化的垃圾回收机制,所以我们不用刻意去释放被删除的节点,只要没有外部引用指向它们,被删除的节点会被自动回收。8 a ]1 A6 u* F3 C, K5 a
如果不考虑插入、删除操作之前查找元素的过程,只考虑纯粹的插入和删除操作,时间复杂度都是O(1)- u( I3 W" J" r1 q5 \$ c
/**
* d9 h- E$ Z( K) p/ U * 链表删除元素, ]0 O: x9 ~# X6 |2 Z
*+ x c( g8 t! g1 Z9 t, r0 _6 t! d
* @param index 删除的位置
0 e/ ~: d. N, {, k6 E7 S' d * @return 被删除的节点
, @" L2 H( J/ s- a: S. U */, p I% v% J/ n/ C- G! Z2 I$ p6 j
public Node remove(int index) {
3 V7 C. q2 l" f5 y; t$ p3 L- |( ~0 O if (index < 0 || index > size) {
4 ~% R0 H. e, G! c0 a( s/ n throw new IndexOutOfBoundsException("超出链表节点范围"); J5 Q3 z4 M: A
}
* g4 r8 R; ^9 @% o* a Node removeNode;
8 j& D% _2 B( P+ A) B4 I& n if (index == 0) {
u) p; _ H" w5 B! R' q0 } if (size == 0) {
: }+ G2 D4 v1 d! q5 z$ ~ throw new NullPointerException("当前链表为空,不可以进行删除操作");
. I: w% l( L8 K& Y }
" e) a+ ~, N+ N" b //删除头节点7 j ~* ^+ M+ ~, u+ j& U
removeNode = head;: c' u$ p+ Y0 s# N ^. f
head = head.next;
1 |8 ?9 l2 S$ ? m: R7 V% n1 z7 C7 r } else if (index == size - 1) {. {$ l! e1 Q# [$ ?5 N
//删除尾节点
. V4 [( Z+ Y' q+ o6 V# c Node preNode = get(index - 1);
4 e. Q* A: ]/ m9 y' ?8 ~: R: Z4 b& x removeNode = preNode.next;
9 X6 a: F! o- m. G preNode.next = null;0 {% Z# Y2 @% B# S
last = preNode;" p+ V* m/ r0 R& Q5 X
} else {
9 _2 F. X: q. q1 K! s2 S //删除中间节点
$ W1 d; y' Z, X3 y; j# N Node prevNode = get(index - 1);8 v# I/ D$ j+ v( \! y( K, D
removeNode = prevNode.next;
, S M8 J" i5 |0 p! L& `" v; E) }1 S prevNode.next = prevNode.next.next;
3 A' [$ W% m V( M2 f }
$ Q) z# S) J* S size--;& k; Q% ]' Q! i- Y, I% i9 |% I
return removeNode;% y l4 I, i) i! B
}
- R& S. A- y5 y) A1 tJava实现链表的完整代码package chapter2.part2;9 J$ R5 z+ d+ R6 F( c& I
f- s& ?1 U" W4 g, h+ G2 z. z/**
9 t% w; X/ T' h; n9 n * Created by IntelliJ IDEA.
9 z/ |) q6 q! a6 b *# _; ?; x" \4 [( h$ `
* @Author: 张志浩 Zhang Zhihao
2 j3 B% G E3 N) X0 u* T: o% W * @Email: 3382885270@qq.com& W1 C6 F |* |
* @Date: 2020/5/3# I5 e) t! e+ N4 {6 [# a: n. H
* @Time: 13:39
& z, h6 v+ T) w: Y * @Version: 1.0 q8 U: U% G! x* x4 f
*/% e W6 g8 j* R% A o- v/ X$ w
public class MyLinkedList2 {
) p! ]. I! L, A4 Z private Node head; //头节点; [! c J) i' y3 y# P* f& K5 I" c- K. ^
private Node last; //尾节点) j9 o$ S* w% t4 [
private int size; //链表实际长度. ~7 l7 X5 D/ R! V [5 u4 l
+ }. d% ^) G' G
public static void main(String[] args) {
# b0 }5 R" ~2 s7 J2 B+ b0 | MyLinkedList2 myLinkedList = new MyLinkedList2();0 i# K, C3 M. m$ Y5 C% i
// myLinkedList.remove(0); // java.lang.NullPointerException: 当前链表为空,不可以进行删除操作) f! u7 d9 m4 O
// myLinkedList.remove(3); // java.lang.IndexOutOfBoundsException: 超出链表节点范围: P; [& \; M$ k5 F/ T+ H' G
myLinkedList.insert(0, 3);; }7 s3 O' N( W8 f* ^% \
myLinkedList.insert(1, 7);/ n- B( n) u! J
myLinkedList.insert(2, 9);
& x" b7 N1 M- }, A; c. a4 j myLinkedList.insert(3, 5);
0 }9 K. M. W4 m2 H! ~ myLinkedList.insert(1, 6);
- q6 A4 Z) ^( e2 C. f0 I( ~2 K myLinkedList.remove(0);: `; k& ?& C M# V5 k3 Q$ k
myLinkedList.set(0, 23);
) k/ d! S0 K: E9 ^" ^( j/ U5 U6 n myLinkedList.output();% y( A _* h7 [4 @6 K
}
2 s8 d1 M% j+ Y/ m# y7 ~- E4 P7 A$ K- `% j- s
/**
1 \# R X) u3 W" w" b# j * 链表插入元素9 B; M! i, O# W2 G- p
*
% D- \% X j5 B: ], {1 X * @param index 插入位置
" ~( i& C3 w0 [" K9 Y; F# J! L * @param data 插入元素 被插入的链表节点的数据. q( R. V& U8 \# I) g
*/6 B* \ o Z+ l) n- v
public void insert(int index, int data) {" R5 J7 t8 ^( M9 D* E, e
if (index < 0 || index > size) {
- H7 w( W4 C% P4 | \% ^* @ throw new IndexOutOfBoundsException("超出链表节点范围!");
g' e& g0 G. Q1 Q( S: x! r8 @* p }) _" l% Y9 F9 I8 @5 D! U
Node insertedNode = new Node(data);6 F: H1 R; Q5 P# V4 N5 L9 o7 L' V
if (size == 0) {
7 d( k# `% m% _4 V" a //空链表
. E* ]- t7 V( V% k0 X. Y head = insertedNode;
& ^. @. r( f k0 z2 \: ]0 a last = insertedNode;8 \+ k% D) ]5 @: Q0 p+ X5 \0 O
} else if (index == 0) {
1 g& D% q$ K/ W* H //插入头部
. s4 @5 {1 Y# \8 k insertedNode.next = head;1 L/ k4 e/ _* V2 w d. C. N) o
head = insertedNode;0 C: ~- J% }# z) Q) ]4 s9 d
} else if (size == index) {
$ J; l. l9 X' f: J //插入尾部* Y; N) G0 u& f: L7 n! K
last.next = insertedNode;
h0 Z9 q2 o1 B# i: J1 ~ last = insertedNode;5 M/ M0 I. ?4 B" A3 i1 J
} else {
) |$ }% @2 K' K% Z, I k //插入中间) z# ~" K( A2 L
Node prvNode = get(index - 1);
; c2 l$ w- S+ H- z insertedNode.next = prvNode.next;2 p' z) _& O- e7 h+ M
prvNode.next = insertedNode; L) t% O$ G, d
}1 q* } ~1 I0 Q2 @: Y
size++;
+ |/ `* z0 W$ S# ?! a R! b2 W }0 K4 }. U% M3 D4 I+ O# I
' I% }2 x# y* e* [2 k7 s
/**
C. t8 z7 m% b% `3 o7 Q * 链表删除元素# h+ d. B2 v5 M6 v
*
5 ]4 v8 N! g7 O5 r- D2 } * @param index 删除的位置
5 K9 \/ o1 o5 \% r7 @ d0 } * @return 被删除的节点5 m+ t6 ?0 n4 l6 B6 ?( J7 N% t
*/
5 ^; j! h# S s6 F public Node remove(int index) {" X& d+ O# [& I; B x* E0 G
if (index < 0 || index > size) {
; T6 x' I# T" A# i. S u \4 s throw new IndexOutOfBoundsException("超出链表节点范围");
* l7 O) F0 [: Q, j) | } f) a Q: O+ A, R! A5 U
Node removeNode;
( H1 n8 u- b/ W if (index == 0) {) R" U4 L7 E# E# S- c; {
if (size == 0) {, l2 S* i# y1 Q9 o' c
throw new NullPointerException("当前链表为空,不可以进行删除操作");7 S* A9 p- U% X7 l1 B6 f! f( ]. K
}
+ N/ X2 A' \% G3 ]; b //删除头节点# V' N5 _0 w. I) V$ a. ^% [! X
removeNode = head;
4 p+ l' x8 W8 r# C0 ?# W head = head.next;7 Z" B( c. c- z" O5 X h
} else if (index == size - 1) {
; S, J) s3 q8 K( f3 V //删除尾节点( Y$ d$ K C2 m0 S& ~. U
Node preNode = get(index - 1);+ u, `/ V& j/ Y8 S
removeNode = preNode.next;
! J) s: Z* V% ~- X8 Y0 Z. { preNode.next = null;" R( G/ ^* b( u+ c5 k2 t
last = preNode;# ]4 u7 g+ ~' S3 x: D2 I: Y
} else {
# m4 g/ g+ p' ?5 V. t2 L4 Y //删除中间节点) m7 w+ H$ C+ `: n- b
Node prevNode = get(index - 1);
q. e K" X6 u- F removeNode = prevNode.next;
1 W9 Z m! t3 I6 ]+ _ prevNode.next = prevNode.next.next; C, p/ i( K; L- H
}' a4 A6 }4 Y+ d; G
size--;
* P4 O+ a: E( ]0 M6 G- w8 G% Q% g return removeNode;1 U5 L: W0 E) K
}
$ [* ?& h1 g( O1 J8 P9 d. Y% K3 f/ E9 S, P
/**
5 B6 c: B, J( D- ] * 更新节点 将列表中指定位置的节点的data替换为指定的data。
, S/ k+ H, h7 L$ _; l *& f3 W% Q5 i* d2 q3 D
* @param index 需要更新的节点的位置
7 Y! p' ~/ |- ^* E4 o) e4 q * @param data 新data
0 {/ @3 @# V8 |+ I * @return 旧data% T0 ^$ X @2 n. {, s9 k2 V! K3 ?
*/, F5 T9 h( \" N' T4 a
public int set(int index, int data) {
- z$ {9 p8 M2 S% Z/ h Node x = get(index);
5 ]' z( g; Q% }- d; k* l7 U0 U# z4 s int oldVal = x.data;
2 M. k ]& [9 L: @% t x.data = data;( X3 g* G4 ^% }; u- e& `
return oldVal;
' _1 d5 t& c: T9 h$ i }& G% P/ G( h3 ^) c# Z c7 \
1 ^! S5 Z* |' w# @( Q% ]2 o /**- Q! W9 i4 U% D# \
* 链表查找元素
/ ?% R. \- t% F; p9 Q6 u% l *% c/ i6 E s% M, I+ { u/ m9 x+ p4 M
* @param index 查找的位置7 D; }8 H+ t5 I. F7 m2 F
* @return index位置的Node对象
5 y2 r5 z% k) C& d */
* v/ }# w, J* N) E* M: z2 k+ } public Node get(int index) {* d, L7 |/ B- W+ D8 w, ~' E) E2 b6 Z
if (index < 0 || index > size) {# H6 ?) a) q) V' ^0 U- s- I3 f7 M
throw new IndexOutOfBoundsException("超出链表的节点的范围!");
$ D. |& T, N( h4 O3 Y( u }
. g7 X* H6 L4 m: o6 T Node temp = head;
/ w1 z0 Q) _3 v1 y2 V! i+ W for (int i = 0; i < index; i++) {
# P) v3 c1 K' L5 {( `# G! P6 R0 \! p, v temp = temp.next;
# v# K+ h: {, r. k/ D: i }& U1 ~( }" E0 {3 c6 {+ _4 U
return temp;& S$ F7 }- G$ j2 |0 v
}
% B! v" L4 V) ~8 y* j. P6 A% _! r* j# R6 {6 I1 {& y
/*** I9 W9 D* Q6 a- V# I
* 输出链表
2 U w& @3 n$ L: \. I */
0 g' e2 Z4 ]) L2 c2 W; k4 W public void output() {
8 h& ~* R# d6 R! X' S Node temp = head;
( _% d7 W$ C3 M D) p while (temp != null) {8 X3 w; y6 U5 u1 u& Z1 _3 Z
System.out.print(temp.data + " ");, z' I% k! {, l# [' m
temp = temp.next;
9 Q u- Q2 L6 m7 Y0 j }
" b+ c7 L4 Y h& T1 Y$ J1 R' z' l }( c' o1 a* `$ b9 ~; R* E* ~
. @# w% d; R3 ~. A8 @: _$ _! H
/**1 I$ l9 ?2 L* v% O
* 链表节点# O- m, V& l- E% b2 \8 J5 F
*/) G& N$ X$ X7 b4 l5 H- V
class Node {# C+ w9 K2 M1 D' J
int data;
7 X2 ^ w. l6 P/ J2 D Node next;
! P$ X- h1 k7 L
& m) W" X' _. I+ L% Y: F/ `5 q' p3 h Node(int data) {- x! P# X+ b0 F* J
this.data = data;& o, y4 [3 k2 W6 I* ^+ ~+ _ I
}/ i2 C x n) Y5 {; @: h8 q
}
5 z, G7 q% Q& k Z8 |}
$ N8 p1 C: I9 D' F6 M A: s. i- _! r$ Q9 F% \) n
5 f2 Z9 j$ \8 ^7 u% t二、双向链表
) @: h0 J- H$ Y# Y P双向链表比单向链表稍微复杂一些,它的每一个节点除了拥有data和next指针,还拥有指向前置节点的prev 指针。
( D ?4 q2 f& P7 H' Q# u
- M _5 M% f4 \! l2 R
6 w. P" t. `) C# T9 \
3 Y+ N9 y0 H! G R1 u5 t% m% V% s/ _: A
————————————————: f ]7 l2 y% M
版权声明:本文为CSDN博主「爱做梦的鱼」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。1 K8 } u6 e, w+ \, w- F; v# a
原文链接:https://blog.csdn.net/weixin_43124279/article/details/105904468, ]5 e) T8 c6 [+ b# {
( i+ U2 B* S7 e9 \) v0 R: a* F; H3 Q
; C& y0 C+ U1 e& ]- U( V$ T( o5 `7 { |