; L; J+ |4 ]6 x【Java演示】什么是链表?数据结构
H9 f6 D6 N+ W$ l一、单向链表
. j$ ?) ], w) D/ k! T1 {( {4 J3 A. i! t. A# t* @2 q; p5 v
9 a6 I' M: ]2 K, F* T
链表(linked list)是一种在物理上非连续、非顺序的数据结构,由若干节点(node)所组成。
6 E0 A; d4 B8 V0 R, ?0 _4 R单向 链表的每一个节点又包含两部分,一部分是存放数据的变量data,另一部分是指向下一个节点的指针next。* ^- H/ n3 l. i8 k
链表的第1个节点被称为头节点,最后1个节点被称为尾节点,尾节点的next指针指向空。
/ b: X* G# B9 e# R7 a9 H! }/ U5 F$ R8 D# l# N
什么叫随机存储呢?
7 W* O0 Z. W6 U+ X) d! W; O6 g4 J
8 z8 w |9 I% }8 O如果说数组在内存中的存储方式是顺序存储,那么链表在内存中的存储方式则是随机存储 。- m0 E4 y1 U; I% k8 ^' \7 x
上一节我们讲解了数组的内存分配方式,数组在内存中占用了连续完整的存储空间。而链表则采用了见缝插针的方式,链表的每一个节点分布在内存的不同位置,依靠next指针关联起来。这样可以灵活有效地利用零散的碎片空间。' ?3 F8 ~. i; V8 a- z
) G' _8 Z, G8 R6 A! v8 h: b
% ^+ G# V) j6 N9 S图中的箭头代表链表节点的next指针。 链表的基本操作1. 查找节点在查找元素时,链表不像数组那样可以通过下标快速进行定位,只能从头节点开始向后一个一个节点逐一查找。
6 m( Z% `8 A& X$ ], {; ~; z, S: K! f% Q& ~
/**
/ u+ K8 K+ x9 U2 C# K * 链表查找元素9 r% K) i+ X# [6 ~( c% H
*3 `5 _- p8 K7 p% D
* @param index 查找的位置
. Z2 o& ~5 y, ]( T4 e$ x2 H6 b" G m * @return index位置的Node对象3 p1 {/ U% j5 E$ F
*/
8 m' h% Z1 P1 H3 d2 g# }8 {# o public Node get(int index) {6 q3 I% ?0 e9 Z7 V
if (index < 0 || index > size) {
. o. Y# E; J) D# O# D throw new IndexOutOfBoundsException("超出链表的节点的范围!");
# H8 ^5 Y3 H& r) R3 i }: W; ^! h4 x$ a& n
Node temp = head;) c- J5 t; g% R# X0 D8 s
for (int i = 0; i < index; i++) {& O+ ^4 T, l, v; L
temp = temp.next;
* f7 [7 q, N; k/ r }
! S' C! Q( h) l+ h3 K$ w return temp;
4 A& H$ R6 o1 [+ W& @* b, H }' {1 e6 w2 H' ^/ U+ u
. Z$ L9 @( ?& `' v
链表中的数据只能按顺序进行访问,最坏的时间复杂度是O(n) 2. 更新节点
. u G# }" G" U- n+ N
3 [! \; u: e l5 b" ^5 d9 D" i如果不考虑查找节点的过程,链表的更新过程会像数组那样简单,直接把旧数据替换成新数据即可。1 ], L# I, E# N8 s
如果不考虑查找元素的过程,只考虑纯粹的更新节点操作,时间复杂度是O(1): G$ r" r5 T! a9 m
/*** [, {* e6 Y& w8 H) ?, j
* 更新节点 将列表中指定位置的节点的data替换为指定的data。
% e# S; n2 Q# \/ \: X5 R *
5 r# p) G( {9 f" x- o6 \0 k * @param index 需要更新的节点的位置
3 y/ B# O" {+ A% c6 d2 W * @param data 新data1 [ e; y$ c- T6 N$ s! q) K4 G8 f
* @return 旧data
1 n. E2 p+ [0 k# ` */
8 o! y$ e3 H( }% Z public int set(int index, int data) {6 X+ I3 d* b, u" C$ H& ` R
Node x = get(index);+ W0 c$ P- E+ G; e
int oldVal = x.data;
2 {+ u% K/ s1 l; e F: G x.data = data;
" M3 p, C9 ^0 d- k& l8 @ return oldVal;' I$ p) \, \, n" }8 I6 v8 b
}
) i. p2 A6 V a# m) U) ]) G7 s8 ?4 i) ]9 ] }- Z- `, b
3. 插入节点只要内存空间允许,能够插入链表的元素是无穷无尽的,不需要像数组那样考虑扩容的问题。 与数组类似,链表插入节点时,同样分为3种情况。 - 尾部插入
- 头部插入
- 中间插入
, L5 {5 B% I$ W2 Q% W9 @- g 3.1. 尾部插入尾部插入,是最简单的情况,把最后一个节点的next指针指向新插入的节点即可。
" Y( }' f5 x/ z0 r$ U( D$ O
% G# U9 I4 a$ g
3.2. 头部插入头部插入,可以分成两个步骤。 - 第1步,把新节点的next指针指向原先的头节点。
- 第2步,把新节点变为链表的头节点。
1 Z2 _' B0 X0 h" u; Y9 K
; U p! P/ w( k7 H3 ^
, Z% z& o, A' H2 N# ^' s5 B
3.3. 中间插入中间插入,同样分为两个步骤。 - 第1步,新节点的next指针,指向插入位置的节点。
- 第2步,插入位置前置节点的next指针,指向新节点。+ ?1 F/ X" i) U: u; b* ]3 `
( u% W! x, Y- A2 E1 H7 w2 s+ d( h; A# @, ~& m9 a" ~; Z2 R0 o) x5 V
三钟情况的代码合到一起
9 O+ ?8 ]/ [+ N* {' ^- r/ G$ s6 `- _$ Q9 f' P- B" I
/**' j" B# }4 w; @5 P7 D8 C
* 链表插入元素 W1 K3 ?" }( K' i3 P0 H; V: k+ Q
*
/ H* U/ Z3 R1 U& _* g+ { * @param index 插入位置 C3 {: T+ C) M# e* Q: k4 k
* @param data 插入元素 被插入的链表节点的数据
D% _/ Y7 T9 L& ^ */, v, K3 |8 I! Z0 E8 G: i
public void insert(int index, int data) {
1 A! o# i( V& o$ ? if (index < 0 || index > size) {& Q/ @7 W6 Z/ n2 U( G
throw new IndexOutOfBoundsException("超出链表节点范围!");! z* [, A$ d8 m- d9 Z
}
. x: r# S$ Q+ K$ ?3 X9 T+ W0 ~2 c, m Node insertedNode = new Node(data);
7 \+ X8 j, O% u& z1 w if (size == 0) {
' P& I$ T$ S* N6 M* d; W //空链表
2 L( w9 g( s' H% ?9 A; { head = insertedNode;) w* ?7 {8 B3 t
last = insertedNode;. s& I/ A: l# L' K: z
} else if (index == 0) {
0 x9 l/ S% P8 x //插入头部/ d! H" z" j. Y) G4 x
insertedNode.next = head;
# U" M$ p& ?' _$ M: M j- z7 w head = insertedNode;6 S& f0 k4 e4 ]( g0 B/ t9 P: b7 Q
} else if (size == index) {4 j0 J R+ r. O
//插入尾部( F8 v* z+ I+ M- ^6 h6 R- W
last.next = insertedNode;% c- M' }# S: h: S
last = insertedNode;
1 ^" @6 o, T7 ~) k } else {* ]% k7 i7 H- e
//插入中间
+ x8 V6 B Z3 \! U Node prvNode = get(index - 1);
/ E- |0 T4 i; R5 `" j, X4 T insertedNode.next = prvNode.next;, K( ^0 u; X) w4 K) C; T$ R9 s
prvNode.next = insertedNode;
, r' M" w6 _6 R, V# b& x! Q( V! T }* _7 @( L9 K% j0 m
size++;$ K- Y4 ~5 l4 [/ q. w" L
}7 Y, C; s6 g- e+ J% b
( P1 O+ V+ w3 v6 \
/**6 r6 c, _- D) r& A
* 链表插入元素& g# X. J" ]! Y" ^
*
1 ^& F4 G) J& x$ X# G * @param index 插入位置
" Z# u7 f. n9 P! l; Q% b. F * @param data 插入元素 被插入的链表节点的数据7 H2 Y; n9 b/ p0 t
*/% S* T" g, r! C7 C) a5 E" i; e- Q' G
public void insert(int index, int data) {
3 p3 O0 K4 X3 \, R1 p3 C. M if (index < 0 || index > size) {! o* [$ \3 l9 K/ P" Q) ?. c( ?
throw new IndexOutOfBoundsException("超出链表节点范围!");
: A- G9 p3 {! U2 b }5 @/ t. }; q& i' ^
Node insertedNode = new Node(data);5 X1 S* h4 O/ Z" }
if (size == 0) {
' D8 w7 }9 k+ `4 V& X j1 x: X //空链表( V8 T; \* W* x# V# y; @3 f
head = insertedNode;
' q- e0 z+ {) u- Q! { last = insertedNode;! q e m2 Z+ o0 `, Z
} else if (index == 0) {
9 |' f( v$ B: A2 v, z //插入头部
# M6 Z8 V, ?' l. y: e" R' j insertedNode.next = head;
2 T" s8 y7 U* S! n! k head = insertedNode;
* K2 _4 N8 C! q! L- J3 ?, J } else if (size == index) {
$ J* `, |( J; e( G9 l( ? //插入尾部$ d" b7 @+ i$ b7 J+ _& ` i. h
last.next = insertedNode;7 C6 z$ a3 Z7 ^2 j
last = insertedNode;
6 _$ ~0 W) }9 N& c! O } else { l5 F* H# {# X: `- ^, P- y
//插入中间
. D8 b [ }) a" ^: N8 }( L Node prvNode = get(index - 1);
3 W3 r: l; O3 s insertedNode.next = prvNode.next;
* k, t9 l4 D; e4 U4 N- F& v prvNode.next = insertedNode;
- Y' ?8 ?$ C! ? }- o% V8 B& k4 k7 I% F4 L9 n
size++;
, c/ M- q4 t; Y0 q }
1 z6 _; j# }2 j% g- Y7 a+ G4. 删除元素链表的删除操作同样分为3种情况。 - 尾部删除
- 头部删除
- 中间删除" G1 w& `; }# }& H7 x
4.1. 尾部删除尾部删除,是最简单的情况,把倒数第2个节点的next指针指向空即
t+ K+ I6 z) n* ?& l, E$ l0 c9 D A可。
, T+ H+ N! g# m5 `
" J g. o; f$ b+ v- d" R4.1. 头部删除头部删除,也很简单,把链表的头节点设为原先头节点的next指针即可。
' @8 {" {, l( f
& @% z: R H5 N1 k) B P4.1. 中间删除中间删除,同样很简单,把要删除节点的前置节点的next指针,指向要$ v1 ~. Z. [3 W) U; ~ s
删除元素的下一个节点即可。
4 M7 T0 l0 e& f. u: o! ]
- r9 ~. f7 i& J: ]8 {; i
; c- ~+ e/ U% _) C% D8 f8 I; M这里需要注意的是,许多高级语言,如Java,拥有自动化的垃圾回收机制,所以我们不用刻意去释放被删除的节点,只要没有外部引用指向它们,被删除的节点会被自动回收。
$ S; J( i& j2 A! g; r$ w' }; P3 [, w如果不考虑插入、删除操作之前查找元素的过程,只考虑纯粹的插入和删除操作,时间复杂度都是O(1)+ T% x$ ?) C7 q) z* u
/**
% B& V* ]0 m0 g * 链表删除元素) l. i% u' B& S9 c
** P! e9 Q% C' p# L
* @param index 删除的位置 k. M# ]0 W+ [
* @return 被删除的节点3 U1 t9 [; M0 r8 [* G) O, ]0 c0 {
*/( A1 X! o$ f4 ^& W( [2 D6 P3 i( D) k
public Node remove(int index) {; m) i# H& t7 R5 {
if (index < 0 || index > size) {3 f- X; w8 x0 J/ f% j; S3 q8 f; S
throw new IndexOutOfBoundsException("超出链表节点范围");
) b$ s h+ n$ o }
0 ~& m% B% l3 @: U. B2 H. z1 e. \ Node removeNode;+ D5 u0 e* o v# H' A" d/ n
if (index == 0) {
, x1 P: b2 K8 J3 j/ B7 X if (size == 0) {8 g6 j- v8 J: W/ W1 h& u
throw new NullPointerException("当前链表为空,不可以进行删除操作");
" F2 {. L/ x" P) T }/ O+ `- J2 b; d1 O5 p
//删除头节点
5 g. Y9 Y' u& \. N1 k% ], E8 I removeNode = head;
8 K( B0 Y# P4 o head = head.next;
' c$ K8 C0 y" s" l; q) W$ ] } else if (index == size - 1) {
1 ]$ y+ z( K3 A% s //删除尾节点
& t y8 l! } O Node preNode = get(index - 1);
# t1 S s9 j5 j" Y2 s: G removeNode = preNode.next;
) c1 ?, i2 @9 }2 K; f preNode.next = null;% k( }8 ~! i$ P; g- k5 i0 G
last = preNode;3 P# u+ z% i3 U( J' k
} else {; Y4 x5 `' I6 R, x0 H* f6 a6 C
//删除中间节点% ~- j- O1 V: `1 v8 G& m
Node prevNode = get(index - 1);
& ^* d f) q8 K$ p5 c4 t removeNode = prevNode.next;
! k1 s( e2 ^+ O' ^7 }9 n prevNode.next = prevNode.next.next;
" _6 C" X4 X% k! b }* Z: J3 ?6 H2 o; O6 j
size--;
% U" c: c4 i3 A1 S6 R k1 ]. m return removeNode;
, ~: T1 P8 t j9 _0 { }
a% s1 n {# S$ G8 mJava实现链表的完整代码package chapter2.part2;/ ?. v, G# Y# z% F" B' M8 Y
* k) t& l# h: H# w6 c/**, P3 s$ V8 L- J6 v7 Z! _2 K9 j
* Created by IntelliJ IDEA., t4 x# Z/ g( @4 t* e3 X& w; ?/ H$ _
*+ x( [& L: f2 {" Z0 i
* @Author: 张志浩 Zhang Zhihao- f$ T7 f/ {. u
* @Email: 3382885270@qq.com
( @' h9 [- O# R! b * @Date: 2020/5/3
/ w+ }+ g7 ]' f! {6 Y, t2 { * @Time: 13:39$ I) j4 L7 N9 t, F7 ~0 Y( \
* @Version: 1.0& K' [$ \ R& _" Q
*/% O! i0 Y, R7 Z' A C$ O
public class MyLinkedList2 {6 ^1 O+ @; y0 c8 d. O; X" k
private Node head; //头节点% w9 @! x. A" m9 r
private Node last; //尾节点
( ?% L# N8 f- z% B5 p, m& r/ p private int size; //链表实际长度
6 E9 ]2 ^) `& K1 D& m& N! x& y5 ?+ h9 v& x' c% {* h, a
public static void main(String[] args) {% L9 I9 m- b# \7 }. p+ B* K- _
MyLinkedList2 myLinkedList = new MyLinkedList2();
1 d' b0 w1 d3 F1 T1 o1 v. {/ E// myLinkedList.remove(0); // java.lang.NullPointerException: 当前链表为空,不可以进行删除操作
1 h; E+ p9 b9 N" |. C8 l! Z// myLinkedList.remove(3); // java.lang.IndexOutOfBoundsException: 超出链表节点范围7 V/ P, O9 Q' Q
myLinkedList.insert(0, 3);2 [5 v& z% B7 A: I; Q/ Z
myLinkedList.insert(1, 7);
: ~) {2 a( l' _6 k5 r myLinkedList.insert(2, 9);; b' I" x! Q b
myLinkedList.insert(3, 5);+ P8 _$ q7 O+ I7 d e2 ^( V
myLinkedList.insert(1, 6);& y/ P8 u# f8 D( D ]5 m
myLinkedList.remove(0);. ?8 w" f- L5 y [8 K+ [1 ?5 z
myLinkedList.set(0, 23);
1 i) |0 r7 G$ G" u* H% i( q myLinkedList.output();
& R$ E% B' R; p( ^9 Q }
: |) C. b3 F9 X* V! S- T% K) @$ r A6 v# y: I* X% x0 ?
/**3 \: L1 g; j/ w
* 链表插入元素
0 s/ q5 c0 N" N4 M! r. U1 L5 d8 ^ *' f6 i# J A/ X/ [: q5 u+ P8 s1 d
* @param index 插入位置/ E" M- P" j5 V7 ~4 a3 c: n
* @param data 插入元素 被插入的链表节点的数据% R( b+ s2 b+ Z
*/
1 N% {- ~9 R$ s+ b# Q public void insert(int index, int data) {2 [* F6 X( a& ^( ?
if (index < 0 || index > size) {
6 T) R1 Z9 Z% p& | throw new IndexOutOfBoundsException("超出链表节点范围!");' z ~, e$ M- | ?7 _4 R. J
}( j) W7 \9 J4 z3 Q9 g7 \! ]8 V
Node insertedNode = new Node(data);# Z+ V8 r" N# h( ?0 A" e/ [
if (size == 0) {$ R8 {; f4 @% V- U
//空链表
8 Z8 T" K* Z# ^* s head = insertedNode;
. ]* P; C0 _; ]$ j- A8 {+ G7 t last = insertedNode;5 S9 t' V: B, I0 A
} else if (index == 0) {
' p6 p0 s2 Z, l3 T/ H //插入头部
1 z5 w1 x# M% A F, T F insertedNode.next = head;
: H! y/ ~- R9 U& i; ? head = insertedNode;; ]! ]4 o& S% {" R% B& `. ^, e
} else if (size == index) {
# |1 S: G6 L# a+ ^ //插入尾部
$ _3 }5 w' y5 L last.next = insertedNode;! m$ o; }$ d/ Z* z
last = insertedNode;" \# @, z4 ^% T; m# x8 e
} else {
, K- }/ u( h, d$ ]" t" j9 y //插入中间
' V$ y" V4 c! v Node prvNode = get(index - 1);! d. o' b/ C- ~& C( V/ E
insertedNode.next = prvNode.next;
3 z- i& k( T# ?7 m2 q+ O prvNode.next = insertedNode;1 K- W/ Q8 i, y( \! o+ U
}
- X3 ~ }% N# V# } size++;2 X- B4 P; w$ j2 j/ K4 A
}
6 R/ x- m8 P' q. X' }6 Z0 ]; e- {% D; v8 u- A# k" F
/**; K0 ?2 I2 o& ?4 w! g$ V- g
* 链表删除元素
q2 F) ^* }" s( [: R) i4 X) d *$ ?% A8 K. y) O- C
* @param index 删除的位置1 a( h! I: e- C, L$ P# ?' `' w
* @return 被删除的节点% p1 N/ _- ^8 |4 v3 g/ f
*/0 [" ]% k* k# H) ]
public Node remove(int index) {
$ A/ R. N2 r$ M if (index < 0 || index > size) {! ~, z6 D7 E* z* K1 ^- h
throw new IndexOutOfBoundsException("超出链表节点范围");- v& l$ u+ Q! {2 D0 i4 G% l5 Y
}$ S) v/ o& D& d7 h' p* U3 A0 r, ^. V, R
Node removeNode;7 T. e+ f; {/ ]8 b' @( R6 e. U
if (index == 0) {# h/ g) I4 Z; I5 E% i
if (size == 0) {
6 m0 L9 B( `1 v) u& H( K9 t. x throw new NullPointerException("当前链表为空,不可以进行删除操作");
7 @, [. y0 x* u0 z K. z' |1 Z* p }6 p! G; V& h- @& P9 f: J0 r
//删除头节点
1 P0 |8 R8 _9 S: E% Q" U" | removeNode = head;5 N+ S: S% \8 |3 ^( k; N+ V
head = head.next;; t" z" S6 u v+ K( P
} else if (index == size - 1) {
+ p f% r3 S9 J& y' \0 N& K //删除尾节点
5 a# J: [, P; R Node preNode = get(index - 1);
9 i7 C: e; F( a# y removeNode = preNode.next;6 _4 ]9 ^3 n4 A3 \/ k
preNode.next = null;
2 K8 u+ |0 ?( Y+ G* Y1 |, I0 z last = preNode;
# u4 F$ U+ U' O* T1 e' a1 j } else {: Q6 H, H; a/ @
//删除中间节点
# b4 K2 @; E5 n& o# Z Node prevNode = get(index - 1);+ [0 W5 h. Q2 a) G' d
removeNode = prevNode.next;. d3 v9 J+ A; E/ ^$ n" o6 U& w
prevNode.next = prevNode.next.next;" ^( W5 P. q4 ?* W3 r1 c6 _5 f
}! r9 I; F& U" u6 h
size--;# w6 E% l& Q3 v- l& t
return removeNode;
4 Z6 e* G' ~8 W& S! f; h6 S }
" _) O2 ^/ _5 {! l+ l9 Q5 l l: N, i5 [% o) Z) s4 o; B" f( |" u
/**4 H5 S6 e) J1 A( Y2 @
* 更新节点 将列表中指定位置的节点的data替换为指定的data。1 ?& q# A! {$ [& [( b' V
*
8 h( P* a# a9 Y& C, N. d * @param index 需要更新的节点的位置
" |2 ^! k( a& i- W s( l0 b: z0 J * @param data 新data( c5 P) ~. j' f. O+ Y6 @
* @return 旧data
) `# F! Q9 |( n- U6 {3 w; ?$ R5 j( g */$ r1 ~, f$ u5 `5 |' c* h
public int set(int index, int data) {
) K* K9 s6 C1 b# n Node x = get(index);
/ a5 K0 M- k3 R6 ?( M int oldVal = x.data;
$ z5 m3 V9 S; q# j2 a; h+ K x.data = data;
: Y% R: V, I1 K9 K return oldVal;! L- }0 F/ Q- ~8 f
}" f0 w8 W! P# s) Z
; }4 a" G8 ]0 [6 r2 S /**
9 ^) d2 ` l! s- \. ~( F * 链表查找元素
9 A2 p& E* P6 Z) o( q *
( I1 V% \0 H" b/ O% w * @param index 查找的位置# u1 O" U7 y9 {. C$ p
* @return index位置的Node对象+ D1 j2 v+ H2 L1 w
*/
% t5 c1 o) s( i0 H# F public Node get(int index) {
7 }, o' {1 d2 ?1 { W- f) ~$ g0 d* { if (index < 0 || index > size) {
6 Z3 u& I( I: s& L throw new IndexOutOfBoundsException("超出链表的节点的范围!");
' X- l1 w* Z4 ] ` }: K$ W% Q! J3 F! T- G
Node temp = head;3 ^' i/ J6 K& J) U1 Y) h% ~
for (int i = 0; i < index; i++) {
+ {" l8 @1 q9 B T temp = temp.next;, L( ~. Y6 H& d: m
}
2 [, n7 c+ C' \! D: E/ A# p8 e return temp;
* X1 P0 {6 N: p }3 r/ ?" s7 S! R( R
: ~; y0 W4 D3 O/ p$ Z8 A /**
* O" w- n- l. H * 输出链表0 _" u5 t. T% }3 K" q; k- g9 _, D3 N
*/' p" q1 z+ w2 t8 a5 P0 O
public void output() { ?* |5 ^4 r; F7 H' E; m" n
Node temp = head;
9 L! C4 }6 z+ O4 a$ c while (temp != null) {
8 F u+ J& x/ I% s4 q System.out.print(temp.data + " ");
: U- ?; C2 H% U6 M' S" g& S& p temp = temp.next;
( }" t; G- @8 l0 I. @5 h }0 ]- e" _* ?1 U+ w' {/ D+ \0 q$ ~
}4 E$ O; m! {6 e' i8 a
: c4 W; O; N8 d$ Y- a$ N
/**% K5 W- ^( Z5 h9 S, Q6 |) ?
* 链表节点+ @5 p% M. ^- y0 g
*/
2 U4 C; P6 Y1 V6 Y. C( g class Node {" [ L) ~" N9 f" W* h( n. l
int data;2 T* k2 n0 X; a- Y2 I5 ]
Node next;
! h( S8 m* T y' n1 a9 o+ s" p. V, q1 k( z1 W& K9 w# \
Node(int data) {
`9 T7 k+ p @6 ?: u" E! N7 O9 ]# x& \ this.data = data;0 v. C" B0 d3 _* l1 n
}
' L% ?3 f' k! n% ]& j! T9 o. C }' c2 W: P+ o/ O% I' R
} m1 K5 J+ a8 `4 ]
# q! e2 U v+ |) g3 w4 U- Y5 X3 W. x
/ K$ M' \* j' Q/ `0 I4 J+ d E
二、双向链表
* W" t, z P* [' B* g+ D
双向链表比单向链表稍微复杂一些,它的每一个节点除了拥有data和next指针,还拥有指向前置节点的prev 指针。% S$ ^) C! G( h' p4 p
$ ~# n4 m+ U. ?& z. W7 X
T# Y( h6 m: @% E3 u0 q! | |
" B9 l, |! i8 ?; g5 A, ?. H4 N9 u0 S/ r
————————————————. R6 N2 o9 a* t' e9 W
版权声明:本文为CSDN博主「爱做梦的鱼」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。$ V: _/ }6 l) K
原文链接:https://blog.csdn.net/weixin_43124279/article/details/105904468
$ \6 l+ u# h( t- x4 V9 B
; _5 `- N p; U7 e$ }3 b6 h7 E) A1 u& i B/ H4 T
|