% x n0 [2 L |1 D# M- }* ?【Java演示】什么是链表?数据结构
6 F/ l: {% f7 _8 F, g/ N一、单向链表
8 X# N B) `0 @3 o* O$ t) D! l
& ` N p* ~! L% z8 o9 b
' \1 W; Q4 _9 c5 {
链表(linked list)是一种在物理上非连续、非顺序的数据结构,由若干节点(node)所组成。
+ q1 T( X2 B; ?3 X' W" h) t单向 链表的每一个节点又包含两部分,一部分是存放数据的变量data,另一部分是指向下一个节点的指针next。: R4 }2 b" P+ K6 ]2 R! N, v# m
链表的第1个节点被称为头节点,最后1个节点被称为尾节点,尾节点的next指针指向空。- y" u ]/ c8 y6 f2 d4 l7 p5 ?
$ G5 Z+ j$ A, X6 h
什么叫随机存储呢?
; X: i! S6 B" r+ F6 J
# \8 ]% z F3 t5 C8 `7 P如果说数组在内存中的存储方式是顺序存储,那么链表在内存中的存储方式则是随机存储 。" ?. _' e2 y7 ~7 s5 Q" I
上一节我们讲解了数组的内存分配方式,数组在内存中占用了连续完整的存储空间。而链表则采用了见缝插针的方式,链表的每一个节点分布在内存的不同位置,依靠next指针关联起来。这样可以灵活有效地利用零散的碎片空间。
) L8 S! k. b) T7 }3 R, W+ N
$ d2 W. T2 L, E4 H
+ I4 l% W8 M& u, N6 [
图中的箭头代表链表节点的next指针。 链表的基本操作1. 查找节点在查找元素时,链表不像数组那样可以通过下标快速进行定位,只能从头节点开始向后一个一个节点逐一查找。
+ n' b3 ?% d8 X B, _, B4 P7 U1 _; ^- Z3 l% t& t) ~
/**
9 S7 {) a& k" o) k- @' G * 链表查找元素6 A3 N! ^7 d( x( i6 w$ t( [: t6 F
*
K5 o( _* j8 {6 D6 v6 I% T * @param index 查找的位置- K, ]! H2 u9 C8 e8 o" d
* @return index位置的Node对象
7 t. w: `$ _9 R0 F6 B1 f* |! `! Q */
( e$ r- w& C2 q. p public Node get(int index) {2 `% N# B0 x4 H0 E' g& Q* e
if (index < 0 || index > size) {( j) t% {. j8 P7 t, \
throw new IndexOutOfBoundsException("超出链表的节点的范围!");
k; E& `# ~3 \9 ]3 a: y. m }7 ]/ \: ?6 F7 J! Q- t" {
Node temp = head;& A4 n* h: j) m( Q
for (int i = 0; i < index; i++) {
4 z5 T' \: M2 B, J" E) A temp = temp.next;
5 W# |- r4 ]7 i1 y! \" ~5 j2 i }. D6 f. H; R+ [% T, ]
return temp;
% J# @( `1 k" M% V& \' h }
' Y+ d3 ~9 L7 z1 L2 V; T
" h7 l4 v% p% H0 G# U链表中的数据只能按顺序进行访问,最坏的时间复杂度是O(n) 2. 更新节点
+ Y3 p9 I% \ U( R) A
+ k& F; B; G( M/ o% X如果不考虑查找节点的过程,链表的更新过程会像数组那样简单,直接把旧数据替换成新数据即可。
. C- ]$ |% F# z& T v如果不考虑查找元素的过程,只考虑纯粹的更新节点操作,时间复杂度是O(1), d- u$ ~, a, q5 V4 `% U2 q0 C
/**$ }# M' c% d6 S/ a" ]# r b
* 更新节点 将列表中指定位置的节点的data替换为指定的data。 D. Q; l9 n& t% q: D, Q
*2 W) [" G0 {+ j8 [) t) S7 s1 A
* @param index 需要更新的节点的位置9 Z+ |( A) T1 n7 B& g. M2 @/ z" a
* @param data 新data% f* w# w5 Z5 e* C+ w, ^7 g6 [) r
* @return 旧data
# n" R% ]" ~& T' q3 A8 i7 v3 G */
8 W3 W% p" I: {( g5 b1 | Y( b public int set(int index, int data) {$ H& E1 F; Z/ O/ Q$ ^1 i
Node x = get(index);7 @0 {, D% L3 j" Y1 L7 N0 y
int oldVal = x.data;
; R& X/ s, d4 Y" X3 t x.data = data;4 o" G, X0 s: V/ m6 O- E
return oldVal;& s/ y6 F* i8 `/ L. p" T
}- R+ [# k, {! j, `" t
' k2 T7 [" I! w- G- S5 B3. 插入节点只要内存空间允许,能够插入链表的元素是无穷无尽的,不需要像数组那样考虑扩容的问题。 与数组类似,链表插入节点时,同样分为3种情况。 - 尾部插入
- 头部插入
- 中间插入1 O; S2 L \6 y, E d
3.1. 尾部插入尾部插入,是最简单的情况,把最后一个节点的next指针指向新插入的节点即可。
; e: y/ j( b: ?% [, W8 s& u. }& Q
) R6 Q6 m! D( X# d N7 Z, {
3.2. 头部插入头部插入,可以分成两个步骤。 - 第1步,把新节点的next指针指向原先的头节点。
- 第2步,把新节点变为链表的头节点。
2 g* \/ J3 Y; ^: p
& B6 I# v1 J) N- z# Z
; ^; {' h5 U2 h3 f3.3. 中间插入中间插入,同样分为两个步骤。 - 第1步,新节点的next指针,指向插入位置的节点。
- 第2步,插入位置前置节点的next指针,指向新节点。
8 Q2 Q( r8 h# W* {1 I. L1 F
' {# m* k- a, F2 X# H2 H7 Y3 F1 z( I8 X: @1 ?; `- ]. j% W2 h
三钟情况的代码合到一起
. E D" k7 H# H2 t) m1 b
1 T* A8 o( x& R- U/ i* [( J! ?: ^/**( Y/ X4 L. p, I/ j
* 链表插入元素
. ~& `. O+ c' b# {& w s& C *; F( Y3 z8 H# S4 b* ^1 E" p
* @param index 插入位置
& r7 V+ _4 Y# L% K3 S * @param data 插入元素 被插入的链表节点的数据+ W1 u0 c$ N2 i
*/+ u9 v; [8 Y3 g* U" o" ?
public void insert(int index, int data) {
' [; J1 B1 l; a* s. n! Z7 b if (index < 0 || index > size) {- s/ M8 `" R( i
throw new IndexOutOfBoundsException("超出链表节点范围!");
Z& k+ x8 E" A9 C8 A( T }! `, I0 G. c% h( Q( Z, F! `
Node insertedNode = new Node(data);
) y% O) ]* l7 W3 `' @0 j if (size == 0) {
8 D& s; p3 e; ]8 a4 m1 ] //空链表5 I& o' Y/ x3 Q: M- H9 Z
head = insertedNode;2 X" K6 ^3 _+ F/ o
last = insertedNode;' @- x* m% ~6 @/ G( D( \
} else if (index == 0) { j2 s: I8 f$ h a" `$ |+ B
//插入头部2 ~$ b* a# f* T8 l6 M9 M% ^
insertedNode.next = head;
) x+ K. r3 R* t8 I head = insertedNode;# p6 _* m6 |, t. \2 l: d
} else if (size == index) {
" g4 @: a$ P, `& j) Y/ F //插入尾部
2 B1 i2 e1 k8 Q/ u& W last.next = insertedNode;
Y9 O3 i8 o1 t. v last = insertedNode;$ g# W2 {; Z! N( P7 c* T
} else {+ `: P5 n: [& W; \6 p' i) G
//插入中间- m9 m/ d+ J/ W$ ~0 k: M
Node prvNode = get(index - 1);& {. Q7 L! O, U* Q
insertedNode.next = prvNode.next;! b% p- h- K4 s8 \) E: y' _
prvNode.next = insertedNode;
3 v8 F# n& p1 h- {& k- K }+ L! w. K) B" ]( o8 j2 V
size++;! _: H" Q: I. I4 V6 `
}+ Y+ |# W+ r. y/ }/ i
# r. k0 {# K3 F0 [ w2 U& {
/**, B9 _1 I+ {3 F5 r% d& c$ T2 M" H
* 链表插入元素
% C. y. [8 V; r. W( S" x9 _ *& i$ B! l. I. g+ i
* @param index 插入位置- ~9 W" W& v) R; {; N' V
* @param data 插入元素 被插入的链表节点的数据
" S6 ] s3 |& R% r */
4 v' G/ u- ^, C ?. J public void insert(int index, int data) {
& p. ], B! ~3 x/ \& ~ if (index < 0 || index > size) {7 d+ r/ @( U" _6 r
throw new IndexOutOfBoundsException("超出链表节点范围!");5 r' n0 M$ K6 L; W! `
}( D8 W3 n( i" m( Q% @/ w+ M
Node insertedNode = new Node(data);
, F& w/ w5 b6 }: N8 `% G) F2 C+ |& n if (size == 0) {
$ g# i6 _; f( M4 o //空链表
; S0 @7 C* [5 L8 Y. S head = insertedNode;" @( a: O& h1 V( t4 g, l
last = insertedNode;0 H1 ^$ L& T$ A( @
} else if (index == 0) {( O0 J# W- C6 r0 ^ B
//插入头部 b- o6 S8 n5 D$ L% d3 X3 i/ d2 g
insertedNode.next = head;
" L& N# t: y0 }4 a6 |" }/ T head = insertedNode;
1 C3 R- R. E. ?; _4 B } else if (size == index) {0 g# N: d' g( f5 O6 K1 m+ Q5 y+ E
//插入尾部' u$ u/ y, j4 w- g
last.next = insertedNode;8 X! v& {. E! _+ s
last = insertedNode;- {, X0 I8 R, U: }# ?7 ]: S6 M% M! _
} else {# \' H8 \: I% a! k* s7 h2 x2 I
//插入中间
- n0 V7 G1 m; b. @0 a* ]4 a; C9 B8 l$ m Node prvNode = get(index - 1);. `+ E. Y# x- n$ J" i
insertedNode.next = prvNode.next;$ L B/ G. o3 u0 N# i w' `! \
prvNode.next = insertedNode;. |3 s! d: u2 Y1 G
}
0 d" P# e# l T u size++;
/ h2 x( v2 j0 V2 Z6 ], W }
3 U6 m0 G3 @# i7 Z4. 删除元素链表的删除操作同样分为3种情况。 - 尾部删除
- 头部删除
- 中间删除
) m; I$ Y" b; h4 R7 n% Z7 \ 4.1. 尾部删除尾部删除,是最简单的情况,把倒数第2个节点的next指针指向空即& |, a; j- M$ }$ x! X
可。
. ~8 [) ^- I: a7 E
8 H% @, W' {" z& E- C) _" {0 d4.1. 头部删除头部删除,也很简单,把链表的头节点设为原先头节点的next指针即可。
9 W# g" z! { D! r; l& j4 c& o
4 a/ e- F+ P7 }! @: ]2 J* \% v
4.1. 中间删除中间删除,同样很简单,把要删除节点的前置节点的next指针,指向要+ I9 k9 R( \# }6 I
删除元素的下一个节点即可。
* a/ F$ w" ~/ ]- w1 K# G/ c; E* p- ^9 o; H( l* s+ i4 \! Q1 S: y" L( E
/ S: T { Y3 q" Z$ C& b, M! q这里需要注意的是,许多高级语言,如Java,拥有自动化的垃圾回收机制,所以我们不用刻意去释放被删除的节点,只要没有外部引用指向它们,被删除的节点会被自动回收。
& C" D0 h& E0 i" z! h2 x如果不考虑插入、删除操作之前查找元素的过程,只考虑纯粹的插入和删除操作,时间复杂度都是O(1)
8 y$ _* O- A, g+ H; S/**( {( h3 E W' ^( V- l
* 链表删除元素
+ P) P9 Z) ~0 _4 M8 x9 N- @" V *3 F9 E$ _, b. \, x6 p
* @param index 删除的位置# c. T {8 H3 w) f% @% ]: w" G
* @return 被删除的节点7 T3 A Q# d* s9 \& ]" I. d
*/
$ A1 X0 N, G" q7 h public Node remove(int index) {
8 v! r5 H* r. ~; o H ` if (index < 0 || index > size) {# m; I J# V1 A5 u \+ S
throw new IndexOutOfBoundsException("超出链表节点范围");
9 ~1 B7 U5 K8 S s }
' ~; ~ g" Y4 l/ v# ]$ } Node removeNode;- V4 V( Z# b3 O8 Z2 p4 ~/ Z+ O
if (index == 0) {3 x$ {4 E2 r: a# H9 \8 J
if (size == 0) { T$ s1 [. T7 h+ J5 t4 m
throw new NullPointerException("当前链表为空,不可以进行删除操作");
1 V! T0 m9 W( q6 l) m$ w }
: Y$ k$ V( i; ^1 ^; i/ e4 {% Q //删除头节点
( v# y: d/ S: e# w- y& Y7 | removeNode = head;
1 U y2 C8 e4 F2 D: X2 y% r head = head.next;$ d8 ]' V, s' f5 m$ V
} else if (index == size - 1) {* w, M4 h- t' a6 {8 Z. U
//删除尾节点
" ]8 O ~& E8 j) p4 f Node preNode = get(index - 1);7 \* [& V# [/ b) o! ]* u. k0 C
removeNode = preNode.next;
: y* P. }4 |5 ] preNode.next = null;
0 I: N w* _5 ~$ [ last = preNode;
q u6 f+ }7 q: g6 K% f } else {/ w/ t/ _' l; \: S& u a
//删除中间节点! k6 x8 v8 }5 F/ v+ T# C/ X
Node prevNode = get(index - 1);5 d8 P6 t2 b' H% i" z$ U
removeNode = prevNode.next;: v6 H$ W4 \8 _* r# K
prevNode.next = prevNode.next.next;
: u" k) a. j* c c, a8 h8 @$ c }( d% e: A/ _5 @* T8 [; E
size--;
5 R: P, _ w+ p1 _% w1 d0 Q: ` return removeNode;' [/ m5 O M; x- J
}
* d" [5 c* {; ~4 D9 ]9 X- @ s1 [Java实现链表的完整代码package chapter2.part2;
! l/ k& j# U- U( `2 r
6 r- J$ P1 ] g1 M+ J2 G' n/ l2 P- ~/**
# v) c! D! g4 p/ A * Created by IntelliJ IDEA.4 P3 `! u1 Z+ D: D1 s' e
*
, y, }0 m W7 _ * @Author: 张志浩 Zhang Zhihao
6 \( j- v0 M6 z r. r1 D3 n6 a0 m * @Email: 3382885270@qq.com
, Y( L6 P4 b$ e4 o7 f4 S * @Date: 2020/5/3
/ _& Y h4 S, j! N * @Time: 13:39$ S( _% E2 ~& Y) T" }
* @Version: 1.04 I! i1 y n% {2 V: P7 {
*/$ e* O% {) P+ b7 i4 J% b9 p
public class MyLinkedList2 {
3 {0 x* C4 S3 e, w0 ~5 q, i private Node head; //头节点
9 @$ D0 n7 {3 J. o V [ private Node last; //尾节点0 K, y: Z( A0 @3 \& s
private int size; //链表实际长度9 W ^# D- S$ I6 V, S
3 m& E' Z+ l5 l" Q9 W public static void main(String[] args) {
* O: \0 C$ U4 S4 L+ C7 Y MyLinkedList2 myLinkedList = new MyLinkedList2();
J: d. W0 ?% w7 w! R// myLinkedList.remove(0); // java.lang.NullPointerException: 当前链表为空,不可以进行删除操作5 U2 o/ i! K4 ]8 M- Y/ Y3 T
// myLinkedList.remove(3); // java.lang.IndexOutOfBoundsException: 超出链表节点范围
) d" K2 L8 i6 o6 o myLinkedList.insert(0, 3);: x! k z( V1 L
myLinkedList.insert(1, 7);
3 Z7 Z7 h0 Z1 O$ s0 D% \8 x1 Q, L myLinkedList.insert(2, 9);% A: {" X& p" |! ^/ L* j
myLinkedList.insert(3, 5);
. w0 H; K4 W& E# [. c3 j% z myLinkedList.insert(1, 6);/ B; @% E2 e; f* l
myLinkedList.remove(0);' X! r3 k' ?6 B! I3 W1 l' t7 S
myLinkedList.set(0, 23);) |; E* J) B* z8 u5 H
myLinkedList.output();
! V( ~! L) R5 g2 j' x5 h2 y$ _ }
* D( f" y! d0 S F: f9 _8 N
2 \, b0 } ]) p1 ? /**
4 D+ X# X9 h4 ], v* N * 链表插入元素1 Y5 ~5 A# i4 f2 W% z+ s
*& z, z" H" n' b, D3 V
* @param index 插入位置( c- q7 \2 {5 Z% T% H* X
* @param data 插入元素 被插入的链表节点的数据1 N4 ?; l3 p/ d0 g q
*/6 ` J T1 H* H' t! T# K
public void insert(int index, int data) {
+ [' T$ P2 R5 l! s" q; ^' C3 [ if (index < 0 || index > size) {5 \0 h. S7 u5 B! c; B
throw new IndexOutOfBoundsException("超出链表节点范围!");- U g0 G* \/ J, l9 }
}+ b9 T3 y* X4 X7 u+ C5 @" g1 @+ J
Node insertedNode = new Node(data);! y# S4 [' N0 O1 N0 W* i# c
if (size == 0) {! H9 F& P/ n9 d
//空链表
9 f4 w- Z& |, Z1 b! C% K head = insertedNode;( R0 ]& D+ S2 r% ^+ t
last = insertedNode;
. O8 o% k9 O0 i0 {* |. O, R } else if (index == 0) {
2 b5 ]$ @" q" E4 Y# `, m T9 ^) q //插入头部
# D3 U! w$ J% G4 u insertedNode.next = head;* y7 A& C3 {! G7 D1 L' m
head = insertedNode;) b. U# y+ v" x
} else if (size == index) {
5 o* G, V: s2 i8 Y4 E5 T( q. l' B0 ` //插入尾部/ j1 w d: @- Z! { R5 _
last.next = insertedNode;
9 T6 ~( I2 `' ] I7 A" I' ] last = insertedNode;
' ~: Q" \, u8 l6 w+ x } else {
" D' q, Z: F1 O x5 q9 s% G //插入中间
1 j) v0 B( n# O' Z Node prvNode = get(index - 1);
3 W# F% Q8 C: r, K8 F& }5 N! K+ i insertedNode.next = prvNode.next;# \" x% G7 w4 s" ?% Y. h
prvNode.next = insertedNode;
+ }5 ^4 e: ?$ i# ?: I. d( o }4 N6 b1 s; M/ C: u
size++;
3 [" m, w- r r: U0 W- e3 L }2 s! r' d6 C) s* }8 C q) [
( e7 ?! I C# l: N
/**" U& o0 o) \* E' a7 E
* 链表删除元素9 w* B0 c0 ^1 U$ n3 ~
*/ G+ m: W. | b! f6 Z
* @param index 删除的位置5 d3 b, n2 o# M/ H5 G
* @return 被删除的节点
2 k( p! K S; j) L */4 |% z- _& m# ^* m4 d/ h. N
public Node remove(int index) {+ u4 i# B% G6 M4 r" S
if (index < 0 || index > size) {
( M5 ]2 R5 T- p/ Q' {2 t throw new IndexOutOfBoundsException("超出链表节点范围"); s) ]" B4 e2 C3 s; z. t- Y
}
3 S; O I- j2 ^- F: } Node removeNode;
% Q+ l/ n# I ~* r if (index == 0) {7 j- N: v: F% y; j( ]* z
if (size == 0) {' y c1 z/ j# x5 f0 K7 n
throw new NullPointerException("当前链表为空,不可以进行删除操作");& H& @4 l3 D7 ^9 ], q
}
, D& U7 m- Z) { l: r) i0 [. e //删除头节点6 c- B8 j# Z9 f2 @) ]2 \
removeNode = head;& t: u7 o8 E3 r$ E( s: x2 k
head = head.next;( Q, U8 |* {/ t( C) F
} else if (index == size - 1) {- R$ Q" o8 @. d" ]/ \
//删除尾节点
7 b- K- V; Q: u& O4 P6 K) U Node preNode = get(index - 1);
# g h- i& `& \3 F6 i) c% @ removeNode = preNode.next;
6 m0 z4 F/ N, J2 j4 B# z1 E! m; o9 K preNode.next = null;5 t7 x. H- C) m0 Q9 x/ @
last = preNode;% ?& F: O! l- g% `0 T
} else {1 i, g$ @9 |. l6 P, o) W
//删除中间节点
/ n' }8 P( r% h# ?2 ?* e Node prevNode = get(index - 1);: i& g* L2 ~ t3 P
removeNode = prevNode.next;
2 h8 u4 V6 w2 @. T+ ] prevNode.next = prevNode.next.next;
1 }$ F: { k9 M! F8 ?4 l, r }
% W0 F* M w0 O7 n5 n- i# U4 ?2 K size--;: q+ W9 V$ C% z
return removeNode;! w) E2 E: W% N
}
$ {8 O) Q8 ]2 s; M. u
+ m: J1 R& f' T* D g4 J' l /**
" \% t0 B% U5 t6 B( U * 更新节点 将列表中指定位置的节点的data替换为指定的data。9 z: ~9 \# H; C" W6 T, D
*
9 N2 t8 E2 j! y# ]9 J0 \ * @param index 需要更新的节点的位置
' D8 d. N0 b2 |/ Y * @param data 新data/ o+ Z! A( m9 L
* @return 旧data8 I! ` M, X$ u2 y$ W0 t
*/0 |9 l7 {2 K' [. l
public int set(int index, int data) {
|; D0 z5 U9 x# Z9 D! U Node x = get(index);
1 o4 R! S9 J8 M6 X K% Y int oldVal = x.data;
' B; Z. D7 C8 T0 z+ b* d x.data = data;
/ w' w5 V/ k7 y return oldVal;6 n6 y8 I- ?. i+ {
}2 F0 |2 i- E5 R, a3 j
: f" B- a; I! {4 q8 I3 e6 x' ?
/**. w) _+ K8 a: N# W
* 链表查找元素9 x8 f% `" i6 M
*
# s6 D7 h3 o+ [2 v3 m * @param index 查找的位置
P9 K8 O: \1 `6 T2 O4 r! V * @return index位置的Node对象6 n: u9 O- M N4 M, f( [+ Z
*/
& c9 g: @$ c/ A public Node get(int index) {9 n. A: p. H O! ~
if (index < 0 || index > size) {
4 |- h8 e; Z0 q; w: u ^" ]2 s throw new IndexOutOfBoundsException("超出链表的节点的范围!");3 T/ ?; v. j0 D
}
7 X5 ~4 u/ R' \7 n( s: [: a& ~ Node temp = head;* O% A; V. f" U) S
for (int i = 0; i < index; i++) {/ ^5 B: S, u5 s' e
temp = temp.next;4 M+ @9 u7 S3 Y5 W
}
]6 w& p6 Y! k G# k return temp;
( P1 @- n" H0 a! I& v }
$ y0 d- O4 |. m0 Q2 o Y
/ b# ~$ ~, k4 e2 b4 D; q /**
5 b" X2 L* q# B5 w! B * 输出链表, W( ?! ]6 `" s+ D1 n
*/
$ G Y! n$ T2 r6 a ] public void output() {
# e( t/ c6 {3 f/ A. M. | Node temp = head;9 j3 \$ n% o. u( f
while (temp != null) {! f' \( w% w9 D9 V3 ]
System.out.print(temp.data + " ");
8 y# K' t5 @% r temp = temp.next;
1 c/ q/ h3 R6 T' O u. d [$ s# G+ ^ }" e. l4 Y3 K( n' @
}8 L- x( P a5 A
6 |, C3 a7 y _+ L! \0 p /**
9 t& t/ L" z3 e) n m5 L" X3 E; o * 链表节点
/ g1 [1 r( x2 O/ I7 ^2 d8 F */
; S, [9 i$ [# ~$ k class Node {
, M- w1 J n+ ~% s int data;3 {) I, t& b9 j! m) P3 v
Node next;# d3 _1 i% V' T/ B4 F2 x* g& A4 v
& Y6 L# S9 w$ P3 y( k+ A Node(int data) {
& x3 e) E' u5 X. c$ k this.data = data;
! t+ Q( F2 T. V- n }" Q: ^ `* b, v# d4 h
}
/ D7 w& ]' R$ Q# y' c9 s3 K( E}
! }5 Z( H! b5 w5 j! N8 v: `% Q1 k. E' V# D1 C8 M% f5 C2 [
5 g$ t6 a: Y2 V6 _' h5 W) \
二、双向链表
4 s* H' Q6 K/ I双向链表比单向链表稍微复杂一些,它的每一个节点除了拥有data和next指针,还拥有指向前置节点的prev 指针。
) u/ Z% t' U* \+ N f. S8 m- I" H
$ ^4 J0 S! x0 u9 q9 R
; c( L, t3 e. M) C; N) W) w
6 I: S5 i4 ^5 ~- p. A————————————————
) B7 f8 n2 L' D! c! p/ p版权声明:本文为CSDN博主「爱做梦的鱼」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。) g. P5 c0 W) |% C
原文链接:https://blog.csdn.net/weixin_43124279/article/details/105904468
+ Z) A! x/ v) F- b0 e& K! m" {$ ~ l
8 T0 d2 I1 Y: T |