* Z0 x0 ~, P% y【Java演示】什么是链表?数据结构
. ]) {- s6 Q$ V% f7 |0 N+ ~) y一、单向链表
6 \1 | m: w: O- ^8 X$ [
( H- Y8 Y5 c# E; q2 }1 s; z5 l0 Q
' I0 X0 v9 v, S. @& U
链表(linked list)是一种在物理上非连续、非顺序的数据结构,由若干节点(node)所组成。
" z' x6 M( C: N$ t2 B' J; c' h8 H! s单向 链表的每一个节点又包含两部分,一部分是存放数据的变量data,另一部分是指向下一个节点的指针next。+ D* M' @4 i, A* t$ b! _" A |8 Z
链表的第1个节点被称为头节点,最后1个节点被称为尾节点,尾节点的next指针指向空。
9 t: t& |/ d6 b' \/ x$ {6 c+ Y) c I' Q' u+ ^7 U5 t) G
什么叫随机存储呢?
/ i: M/ w9 t0 [ w
. A/ j X0 f2 O) E w如果说数组在内存中的存储方式是顺序存储,那么链表在内存中的存储方式则是随机存储 。
$ D9 x4 a6 ]1 Q6 I5 s上一节我们讲解了数组的内存分配方式,数组在内存中占用了连续完整的存储空间。而链表则采用了见缝插针的方式,链表的每一个节点分布在内存的不同位置,依靠next指针关联起来。这样可以灵活有效地利用零散的碎片空间。
9 q7 }0 C; t& r# r7 P
; C& o7 T0 t. U, o+ `
8 y; |) {* {/ K/ l图中的箭头代表链表节点的next指针。 链表的基本操作1. 查找节点在查找元素时,链表不像数组那样可以通过下标快速进行定位,只能从头节点开始向后一个一个节点逐一查找。
9 i' v$ O* G: U3 [8 F! j
! E+ y) u' a4 D/**
; v5 y" _- j7 u+ p$ [1 O1 z * 链表查找元素4 G& X7 a, u$ O$ A* W
*( {. H6 s1 L7 a0 [4 }) C" S1 R
* @param index 查找的位置8 Z8 y! d% Q5 o
* @return index位置的Node对象& h/ [1 A) R* U1 u( x2 Y( p
*/
% j7 n" H K1 V public Node get(int index) {
( J6 |- p; a7 x# e, k* H3 o0 l, L if (index < 0 || index > size) {& r) n, X4 K; _' D) X
throw new IndexOutOfBoundsException("超出链表的节点的范围!");
, Z$ A, g; k b& L: y }5 t4 X' Q f, n- O' a/ m
Node temp = head;
- P- g: A3 W/ O: U; t! q6 k$ }$ x for (int i = 0; i < index; i++) {
* q4 ^* M7 X- z D( c temp = temp.next;
$ m) ~9 [ x2 z, W! S X }) m+ ]* W! G7 _4 ?) _
return temp;
+ L z! t o3 g# ?5 u& J+ e }$ ]" g' y/ K: Z9 E$ S5 V
2 z3 p+ H* @& c1 o+ R
链表中的数据只能按顺序进行访问,最坏的时间复杂度是O(n) 2. 更新节点
2 ~! ?4 i) ^" T1 i! H
9 E1 a* p3 l6 W/ Y. t如果不考虑查找节点的过程,链表的更新过程会像数组那样简单,直接把旧数据替换成新数据即可。
9 Y7 m7 ?; E3 z如果不考虑查找元素的过程,只考虑纯粹的更新节点操作,时间复杂度是O(1)) B5 m% G: |& O
/**2 S0 ^ f: g- H- w* f
* 更新节点 将列表中指定位置的节点的data替换为指定的data。
- m7 Q8 \; O" X1 t) x$ W' ]; k *+ L2 `- Q0 H1 m$ l9 ~/ a, Q
* @param index 需要更新的节点的位置
u4 f* {9 ] q( _& t( h * @param data 新data
1 U- h+ H* I/ u0 l3 t c( b * @return 旧data- n% z4 o6 A2 J3 Q0 F ]
*/) p0 ^& ~7 j5 E2 Y# Q$ g
public int set(int index, int data) {
3 T* |$ v$ X0 w; y% N3 G5 X' T Node x = get(index);) g/ S4 a4 \- J7 r7 [
int oldVal = x.data;) m/ a4 B2 }5 @$ T9 O' j4 ]
x.data = data;
$ S- y9 f# |+ C+ L @" g7 D return oldVal;
. w! P4 G* v3 g. Z }
% c3 d F a, x8 \. h" \
9 \9 e7 U0 i4 {" ~3. 插入节点只要内存空间允许,能够插入链表的元素是无穷无尽的,不需要像数组那样考虑扩容的问题。 与数组类似,链表插入节点时,同样分为3种情况。 - 尾部插入
- 头部插入
- 中间插入
+ i/ S, U6 a/ M' f% a 3.1. 尾部插入尾部插入,是最简单的情况,把最后一个节点的next指针指向新插入的节点即可。 $ {$ ?9 n% `) X
, W0 }# V2 f& s3.2. 头部插入头部插入,可以分成两个步骤。 - 第1步,把新节点的next指针指向原先的头节点。
- 第2步,把新节点变为链表的头节点。
$ }/ J" v- r' X# C
/ T, g8 T+ P7 B! _6 O* K3 {7 j
2 y; W! Z) O( |5 R" M/ K3.3. 中间插入中间插入,同样分为两个步骤。 - 第1步,新节点的next指针,指向插入位置的节点。
- 第2步,插入位置前置节点的next指针,指向新节点。# T; j, M o) o) f$ A% a# I: Z4 J
/ i6 d0 Z" P; O, _/ X( i
Y# Q0 ?" t& |/ y! i7 A6 p) \8 I
三钟情况的代码合到一起
2 R, o% B2 t. c5 B, ]$ f8 @& i, d4 u+ B# _+ h$ W( E! W. u% W% p. \
/** x$ G) k" C7 y1 ~% w
* 链表插入元素
& L2 c d0 O0 A" ^5 o) v z ** X( w+ G# _& X. p7 f
* @param index 插入位置4 v/ d( x2 D1 ^5 a0 s1 q
* @param data 插入元素 被插入的链表节点的数据% v; S- R& B* B$ o v8 @: S
*/
# h/ v. P1 K, r9 d% E9 @! u public void insert(int index, int data) {1 ?& Q0 X$ u6 Z. n$ w/ R' l% O- n& Y
if (index < 0 || index > size) {" A: j- W, z0 v& k$ T* q
throw new IndexOutOfBoundsException("超出链表节点范围!");
O* Q& J4 j+ E4 o; s; v }( v8 g9 w, Q( [' }1 y
Node insertedNode = new Node(data);
" T' D6 Y0 o2 ~/ q8 A/ Q! t0 s) W9 l if (size == 0) {
+ [% P& X! T' d- m1 V6 t6 N d% b& E //空链表
# P( v2 `# Z0 T: f! j head = insertedNode;+ y$ K$ }: ]: b8 O# d
last = insertedNode;
; o, U, u4 u4 b0 ]1 C9 b } else if (index == 0) { e" g! T% d9 @, H- W' ^3 p
//插入头部
2 |' ^. k" F5 F+ l insertedNode.next = head;
$ ]1 |6 @) e* F6 _8 A. p1 ]; \6 G head = insertedNode;8 K" t: M7 ]. N; R; Y8 H7 P
} else if (size == index) {& _0 c; G" Z" v1 ~& {( I/ _
//插入尾部* K$ G+ `$ y7 W& u
last.next = insertedNode;
7 ?6 m+ S# Q9 O. F w/ i) S last = insertedNode;3 I7 s" v. d1 _, ^( m3 @( ^
} else {
9 }# A- m5 a9 u2 S9 h //插入中间0 G* N# [0 N B/ T2 y& c+ C
Node prvNode = get(index - 1);) L' d0 g, N' X( T) i4 X
insertedNode.next = prvNode.next;0 E( B: v( P! j- M
prvNode.next = insertedNode;
& ?# m7 y! E" t! d }; ~7 k3 I) [. _5 t0 P3 C1 ]% t
size++;7 Z4 k/ ^+ \6 a' P/ `, n1 U; S8 X
}+ ]3 B6 m0 j; \/ r+ }* ]' `9 ~
" r- A3 |! g7 M0 ?8 x( j) e/**
1 T( U1 ~) i* j$ K' @9 d * 链表插入元素( W I7 ]; E6 x$ d* T( }
*
; `1 H, [* {/ A6 F* J * @param index 插入位置" _* [, [; T: a8 U# B2 n
* @param data 插入元素 被插入的链表节点的数据2 t' P2 E+ t* e8 B% B
*/$ H, V) ]% ?2 v& L4 U
public void insert(int index, int data) {4 }+ C" j4 y/ p$ F. N; ^. p
if (index < 0 || index > size) {
8 q5 E2 D& R0 P throw new IndexOutOfBoundsException("超出链表节点范围!");/ c+ p. B! A2 b3 i7 u) T
}& L; p* f) B7 \2 U( f
Node insertedNode = new Node(data);% Z3 L$ g( t4 K- p9 b8 R2 J
if (size == 0) {* p( o7 a7 J- f1 I, p$ g4 s
//空链表
! g7 V1 y* _- W" A* v- i head = insertedNode;1 t% F; S7 b# x% b
last = insertedNode;
8 P! Q/ G: W3 {& L( g } else if (index == 0) {6 U+ H" q' w' q# f& `% u! r
//插入头部
$ {; X- Q9 E2 t; b, \' E1 K4 q insertedNode.next = head;( b; n% r, O# v9 n/ f1 u
head = insertedNode;) Z9 e- Y2 g9 a8 m+ F; | x
} else if (size == index) {
; y: w; r9 t9 K3 _3 b //插入尾部" R' Z: ]% h1 |
last.next = insertedNode;& ?. o1 ]5 z( o5 f i5 B2 H6 e( C
last = insertedNode;
9 @2 u3 O6 {4 z$ M+ Z2 W# i b } else {
1 e, v# z8 Q c; ~ //插入中间
; E4 H+ ?0 \2 H% y: M+ J Node prvNode = get(index - 1);( \; [; h; b+ c1 [1 ~
insertedNode.next = prvNode.next;" K+ L2 B c! ^
prvNode.next = insertedNode;8 t8 _7 |0 s6 c
}; Y# f4 ~! V" Q6 W- g
size++;% R B: x3 X0 A4 x8 {
}% r* ]9 ~' T6 C. ]8 d4 k
4. 删除元素链表的删除操作同样分为3种情况。 - 尾部删除
- 头部删除
- 中间删除
/ E3 A: i) Y! R7 I% w 4.1. 尾部删除尾部删除,是最简单的情况,把倒数第2个节点的next指针指向空即
' x% p3 @% k/ U j1 N可。
2 ^# i* [) d3 |: p. |* N
8 v# L$ y& ^# L4.1. 头部删除头部删除,也很简单,把链表的头节点设为原先头节点的next指针即可。
+ p4 V% y$ g9 _8 y& u
( a R u8 K, n. b: r4.1. 中间删除中间删除,同样很简单,把要删除节点的前置节点的next指针,指向要4 J/ l# P6 Y+ k P
删除元素的下一个节点即可。
2 Q2 W8 |( E/ a% j0 C
! I0 D0 N. m9 ~
! g) s {, [- `) B* N$ I1 {2 e" [这里需要注意的是,许多高级语言,如Java,拥有自动化的垃圾回收机制,所以我们不用刻意去释放被删除的节点,只要没有外部引用指向它们,被删除的节点会被自动回收。
+ @' ^1 r" ^2 b% t; l& v1 o如果不考虑插入、删除操作之前查找元素的过程,只考虑纯粹的插入和删除操作,时间复杂度都是O(1)/ S( F, I* j8 v+ Z$ s% O
/**
' D& Q- `* K, I, V- i% {% H * 链表删除元素* C6 s$ W' R3 N7 L' `$ X( _5 T) h
*
/ P7 E' P# f( C6 @5 h5 l * @param index 删除的位置: A4 q! J" O$ }; r: t. y5 h
* @return 被删除的节点
3 M/ j. f! ?# q% M: ~+ y& M+ z *// c% P" S" h- r& }, R, ]9 k% L* V
public Node remove(int index) {) z! }+ ]: T' I5 @' }
if (index < 0 || index > size) {+ G1 ]$ [7 K' G% [
throw new IndexOutOfBoundsException("超出链表节点范围");# j4 w! r( I0 K# V* |9 n1 L# \
}7 d- e8 X6 N' t t8 V
Node removeNode;( D# x" ^. y, }9 ?* R, \/ Q
if (index == 0) {
& F* x" q7 X/ } if (size == 0) {
3 t9 j+ |- ~( y" Q$ F) d throw new NullPointerException("当前链表为空,不可以进行删除操作"); g. S5 q" T" d; {8 J8 J
}2 \* [+ l. p4 T4 u
//删除头节点1 F7 U9 |3 Y$ G$ u6 k' ~
removeNode = head;
# A0 W' U; I) q m head = head.next;
5 D( U6 K7 V' B } else if (index == size - 1) {
+ T5 z: `# \1 m6 i! A //删除尾节点) y& Q8 j; q' V
Node preNode = get(index - 1);
4 {: K6 L" S; \, F2 M) ]3 {5 V$ d removeNode = preNode.next;
/ e, i P5 I5 a( D" n' z preNode.next = null;2 G+ p' [$ j2 e2 v" w, C9 f
last = preNode;2 j0 p% }5 U9 W( o
} else {3 R. v0 C4 {: T
//删除中间节点
$ F$ `2 D0 a4 b8 n2 v* x( o) X: \3 w4 C Node prevNode = get(index - 1);" c' h3 a3 f+ K- W! B; I5 I
removeNode = prevNode.next;
. M6 r1 f$ x3 i% A7 g prevNode.next = prevNode.next.next;. @/ v$ B9 l9 d, v0 [
}
' e& S4 T% A9 M8 V5 A size--;* i' F3 Y$ o" V( _2 }* i
return removeNode;
1 p, v% w: q) U% Z }
. p e8 s; \; S) @" DJava实现链表的完整代码package chapter2.part2;
7 }- d' c" ?3 i" _3 n9 ?2 O \4 z, s2 O+ i; g3 f
/**, o: \3 f5 }; @. q! r9 b
* Created by IntelliJ IDEA.
7 E8 x: b7 k1 T( ?+ Q# N *
5 K4 g, D0 W7 n' A( k& K * @Author: 张志浩 Zhang Zhihao9 ~ d! e0 ~; \) k
* @Email: 3382885270@qq.com+ A2 G' u. O" m* B6 }
* @Date: 2020/5/3# K. f+ O1 u2 r. I" ~4 p
* @Time: 13:39& T- Y6 D% x% x5 }$ @5 w. M* a
* @Version: 1.0# V! E. X4 `; W7 A7 H* v
*/# p" Y; ?/ M3 m2 B1 u9 d
public class MyLinkedList2 {! A6 t: A2 i- [1 S
private Node head; //头节点# L6 K: J3 ^- ~# E( T
private Node last; //尾节点
7 c! i. r( O' P2 T. _. j8 | private int size; //链表实际长度0 E) a# \4 L+ ]8 O' u- s. p- T0 D2 e
4 ~1 K' f1 \9 `% W+ {
public static void main(String[] args) {. j3 l! c2 ^4 q! B8 j+ T5 p" _
MyLinkedList2 myLinkedList = new MyLinkedList2();
' y' Y5 b9 w9 c// myLinkedList.remove(0); // java.lang.NullPointerException: 当前链表为空,不可以进行删除操作. }6 Z$ i- R+ N. A; O$ J* C
// myLinkedList.remove(3); // java.lang.IndexOutOfBoundsException: 超出链表节点范围
0 @/ z9 W9 S* h- K0 ^) e0 R$ t" h M myLinkedList.insert(0, 3);' R$ @8 M1 f* r. {
myLinkedList.insert(1, 7);. M8 h9 I" j+ p; D
myLinkedList.insert(2, 9);/ q ~/ R8 s/ S6 ^( I( ?
myLinkedList.insert(3, 5);
$ [6 A: C/ ~- _# {, S myLinkedList.insert(1, 6);
3 U7 X, C/ Z* o P myLinkedList.remove(0);; Z: N5 T& r5 e
myLinkedList.set(0, 23);
+ [6 W- r) |: @1 ?2 q. y- o( y( |$ l myLinkedList.output();# |9 @0 ^/ s% i+ D( B' a
}
4 j5 D0 X5 l ~# }9 X, t3 P& E, j1 i/ Q7 R$ I2 |0 o
/**
# M# \+ |' ^) {, [3 r * 链表插入元素- `* U* z A8 i* v- m
*9 @+ _, ?5 q& A6 o: m
* @param index 插入位置
0 P! ?4 \- [# m2 {" i * @param data 插入元素 被插入的链表节点的数据
, D& f9 T- j0 h1 D( e */
* ~- [: d, S7 K% r- X. @! u& B public void insert(int index, int data) {8 u/ z8 F/ V; @3 j& ~8 i
if (index < 0 || index > size) {- O ]# I6 \- ~# \( F: f9 j( q
throw new IndexOutOfBoundsException("超出链表节点范围!");, O* b4 r; ~& m' k
}# t# A) b6 G" Y: j7 m
Node insertedNode = new Node(data);% [- W E9 `5 g" V4 x
if (size == 0) {
% j$ @. C' k, F1 |6 x //空链表& X2 d7 r0 C. C5 i/ n; b
head = insertedNode;7 ^5 _- l- e( x% w+ Q
last = insertedNode;
, \& j Y- ~) R" M. E* e& n } else if (index == 0) {
+ z- Y( @; K0 {0 }) e) \$ [ //插入头部% }% m' l3 y4 m) {9 H
insertedNode.next = head;9 [" n" [# s' ^
head = insertedNode;
6 c* C9 r& ~! L } else if (size == index) {, `, A: K, U0 r' b; e) s$ n
//插入尾部( V9 `0 `: r t. J6 N5 I1 H
last.next = insertedNode;
+ _5 L2 \# Q2 i! }& A$ ^ last = insertedNode;3 x9 h2 i, r. ~7 Q9 _5 Z
} else {
$ w3 f$ f, n; ] //插入中间0 q5 L" x$ j9 Q* Y4 b3 K2 _
Node prvNode = get(index - 1);
3 l! @$ O' h* u1 M. E; N5 D& l insertedNode.next = prvNode.next;2 R: v# C2 K' {* u( @& I. C' Z: f
prvNode.next = insertedNode;8 F2 u# ?4 J) n: r
}# A5 W; W' |0 }. }) T, o' P3 z" w( H
size++;
7 H, k3 s1 \+ L n, ]- H }2 s, F/ U n: x+ }9 E2 @
! t z' A8 A# M% s5 S# r
/**( p) r: r9 ^3 X: Y( u, A
* 链表删除元素1 T; s( L. q" D$ j4 t! A2 F
*
# j$ B6 x5 W9 N v * @param index 删除的位置
- r, O' T( b. p5 } * @return 被删除的节点
0 K1 g/ l8 T& n8 c */& W8 g* q7 M- E+ ]( B5 x( f7 y
public Node remove(int index) {
4 l7 g4 z" e- R, N/ C if (index < 0 || index > size) { r( A1 P$ Z D# r# U3 s
throw new IndexOutOfBoundsException("超出链表节点范围");2 k2 R4 U6 L9 u% B# E
}* t* p$ M. ^" K( ~4 v, z2 z, w" M
Node removeNode;- l+ b1 W, g4 Q& Q* @* e! I
if (index == 0) {
2 B& B: O" D- F; R if (size == 0) {
+ u( U8 R8 Q( v ^ throw new NullPointerException("当前链表为空,不可以进行删除操作");
2 @; R* w( Q/ r3 D" a }& {8 S8 q! h2 y% P' b8 F
//删除头节点) ~' y$ ^* S, s' {
removeNode = head;( D3 v, w" _) F9 h0 ]0 M0 C' v
head = head.next;6 e: ~ e1 P5 @, L8 P* }
} else if (index == size - 1) {
h I7 {0 [7 P5 U" k% n& e //删除尾节点
3 Y% A0 z2 S4 t9 U( _3 v Node preNode = get(index - 1);
+ G4 q/ o* G; p removeNode = preNode.next;
S# `6 B" A! J# g preNode.next = null;' x5 P ^( t0 c+ e- m1 T
last = preNode;
; u- G: A; w( z2 D0 D( O* F } else {
2 x. V; e7 e1 x0 E; ?- c) y //删除中间节点
7 `' A! J* v% x/ `7 G2 F Node prevNode = get(index - 1);. N0 k8 F& L3 T: D
removeNode = prevNode.next;
: ]6 _$ N- O- P, g6 m prevNode.next = prevNode.next.next;
4 @( K6 @/ ~ v$ \% E: E }3 N4 U; W5 S1 o
size--;
- y. v3 u5 y. F( b6 R$ k3 f return removeNode;, o: A3 Y" T* V8 R* [3 G' v
}
- D% i+ Y+ H0 `; t$ m& j b" O" a# U9 s9 g1 ^
/**
- {# D, K5 p" {$ Y4 a * 更新节点 将列表中指定位置的节点的data替换为指定的data。
" n! ~4 @: R# R5 e4 t' q *
5 w# _6 ~" ?& b0 e- l( | * @param index 需要更新的节点的位置
( J" S; i; h$ D. ^$ E( i9 w- l * @param data 新data
+ P5 y" t* `# ?1 ~7 v5 X; `7 B) K * @return 旧data6 l/ o. f$ P2 @
*/
6 }0 t& { V/ I6 s; O7 T$ o public int set(int index, int data) {
% O7 R; o; Z6 E% S Node x = get(index);( a# f) ~2 q- K* _
int oldVal = x.data;
. F. z3 ^, U5 L x.data = data;
# e; Y# l3 z. a3 p1 P3 H! J return oldVal;! @8 X9 h. L8 K- c+ c
}5 c( L7 x6 A9 b, A* v# L: _4 z8 I L
! O) C" R0 C; h0 g: S! n) E- i /**4 _2 O4 J6 ^+ Y$ j
* 链表查找元素/ D F- }, p R4 e5 j5 R
*! |0 x; g: z6 v* d8 O# E6 R; \6 f
* @param index 查找的位置
) h4 o+ x; \* ~ Q7 x. Z# d * @return index位置的Node对象% a' V8 H" R/ N* G
*/
( r6 W/ Y4 \$ u2 T" c* R# l public Node get(int index) {
- W/ T$ o. X: U, @+ e4 i' R if (index < 0 || index > size) {
8 V7 U: c* W$ z) B% _( ? throw new IndexOutOfBoundsException("超出链表的节点的范围!");
( n, v, v! v2 ?# _6 o/ s' R }
7 q. V1 F4 G6 }: U9 X3 e% F( S Node temp = head;
3 z& B% `- _+ T+ v9 a* ] for (int i = 0; i < index; i++) {, T6 D2 d+ G# ]2 d$ N. q
temp = temp.next;; s/ g' Q) `. w2 f& c! `
}/ E/ ~6 D; S2 }
return temp;
. I. x, S: \, f* W2 r) w. T3 H }
( C, E8 H) T# J* }- \% Z* S) q5 q! a9 Y2 M! S
/**
3 y. E" P2 `, M) T * 输出链表
$ g* B. l6 h# F6 f2 E0 e( k */
+ r9 c: c# d5 c0 x3 N public void output() {$ e* j9 X2 l9 s$ F- B) ^- R
Node temp = head;% Q, x$ X* X& o( c% a# a
while (temp != null) {5 h `+ D) L: q5 b" A) w" g
System.out.print(temp.data + " ");/ ^0 \; Z+ y/ f( R
temp = temp.next;
( r O- i& A* }, h& G% I c) d }% h1 J# k3 t$ H- I/ u
}
- {# [( r' h. e/ {$ h
4 d8 Z5 \% [+ z9 Z9 U t /**& B) _# z$ [2 ~2 J2 x8 M2 T
* 链表节点
* o: Y" a* u6 E* ^5 U: r *// S/ ]# n( Y0 e) C& H
class Node {
! Q4 B3 z2 m" M) z& [3 U int data;
9 G$ S* R. m t% W* i$ {+ H! C Node next;9 s1 y3 i, y. p6 I" ]9 B
) ^; B4 S+ G6 N# {, O Node(int data) {8 Z! O% Q* o, t" r& r
this.data = data;
) M/ E1 m" h, e( B" U; W) @) g% |+ P }4 Z, ^7 ^2 V _2 I8 Y3 T2 t( [: v
}3 w: l: d3 A# }* l' u
}, W+ P! o% ?# X U
) \5 M( Z- Q* w+ }5 p% g1 P0 N! d) l& y0 p+ E; v; c
二、双向链表
9 G0 L) N# }, W双向链表比单向链表稍微复杂一些,它的每一个节点除了拥有data和next指针,还拥有指向前置节点的prev 指针。$ a4 ?( X6 |/ L, p" W. K
7 `3 ~: o; z' \+ F9 j/ ]5 t$ i2 D7 }* A5 V3 E4 d
+ V& T9 t9 i( k: p) e
% _/ F4 \$ t$ `* M( ]( D————————————————
1 j1 F/ q5 d! w+ w版权声明:本文为CSDN博主「爱做梦的鱼」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
1 j# } z% x6 U) [* [原文链接:https://blog.csdn.net/weixin_43124279/article/details/105904468- j$ D5 J4 t* Z
" L3 m7 \0 b5 r9 ?
9 T1 Y. _) m! N6 `' i7 g |