; m0 o6 J1 |: v5 w* y
【Java演示】什么是链表?数据结构7 `+ }* G1 H2 D' H
一、单向链表
, C9 n0 }! d7 t2 e; n
3 \% D" Z+ T/ A. G, |! s( |
% X2 h% Y) v* @; ?! V& Z0 Z( U链表(linked list)是一种在物理上非连续、非顺序的数据结构,由若干节点(node)所组成。1 D# b( B# v$ G" c; t: N& e( q
单向 链表的每一个节点又包含两部分,一部分是存放数据的变量data,另一部分是指向下一个节点的指针next。- E5 }# u! U! r/ }4 V
链表的第1个节点被称为头节点,最后1个节点被称为尾节点,尾节点的next指针指向空。+ B/ g9 O, G1 T
% H/ e& Q9 P; N% r+ H7 |3 H- ]什么叫随机存储呢?" v4 ~0 c) U7 R: _ O. J. I. i6 T% `$ M
) F0 C2 p" h: }8 ]: a. A
如果说数组在内存中的存储方式是顺序存储,那么链表在内存中的存储方式则是随机存储 。1 F5 N; {. F" V; z( N
上一节我们讲解了数组的内存分配方式,数组在内存中占用了连续完整的存储空间。而链表则采用了见缝插针的方式,链表的每一个节点分布在内存的不同位置,依靠next指针关联起来。这样可以灵活有效地利用零散的碎片空间。3 D. [; M0 R! A4 c, J- ^
. C$ s1 a) U4 c& P$ W! |; m
. k9 u; y+ b! B/ v- h3 u' c图中的箭头代表链表节点的next指针。 链表的基本操作1. 查找节点在查找元素时,链表不像数组那样可以通过下标快速进行定位,只能从头节点开始向后一个一个节点逐一查找。
) H9 j0 \4 x- r0 v8 C8 d
! c! g+ i* a, _& z, S
/**
, {" m' z# ~6 H# R S9 U * 链表查找元素
6 _* p4 k& s/ ~8 l5 J) U' h */ `% S5 J2 K3 |+ H
* @param index 查找的位置. o- ~4 c$ P W* `! @: X o0 ?
* @return index位置的Node对象
9 Q' V, r7 O) Y C- g+ r */
- l. Z, A4 O/ u, r0 P public Node get(int index) {
2 _; P, L) c. Z9 T if (index < 0 || index > size) {4 e$ Y4 | M6 n) l& ?% p& ?" x4 x
throw new IndexOutOfBoundsException("超出链表的节点的范围!");
: a2 y$ G! ^# {* y4 v }' @ V& `' J8 _% w& H
Node temp = head;
0 ~1 w/ X, |+ x0 }2 n% n for (int i = 0; i < index; i++) {) I" l5 W5 Y* L
temp = temp.next;
: P/ W8 ~( n. W/ @% I# C" w }: j' m" i8 f( J7 r y4 R4 w
return temp;
4 |/ _5 u) P" f0 C }
& X. L) W- i# a1 i9 [/ Q/ A% [2 O/ O3 y8 ~8 v( V6 J1 Z5 M4 R3 b
链表中的数据只能按顺序进行访问,最坏的时间复杂度是O(n) 2. 更新节点
( ], R+ w1 x2 J: l- f
) O R0 n$ a5 G* t
如果不考虑查找节点的过程,链表的更新过程会像数组那样简单,直接把旧数据替换成新数据即可。# t- W7 }, q& L, Y- I1 V& P5 G2 m
如果不考虑查找元素的过程,只考虑纯粹的更新节点操作,时间复杂度是O(1)
9 r* ?3 [( Z3 [) z2 A' U4 U5 q# C- V/**
# w' O# j( C2 \; C * 更新节点 将列表中指定位置的节点的data替换为指定的data。$ z4 v) l" g8 C9 n' i* ?& }3 S
*
' S P4 @# X K! c/ ?: |/ }8 v! ^ * @param index 需要更新的节点的位置7 ]# _8 p" U% d- I- J
* @param data 新data9 z; y$ O6 m2 l/ G
* @return 旧data, r5 _1 f4 ~3 b m6 z7 d( e
*/8 I6 H9 O) i+ i+ |+ d% e; H& Q
public int set(int index, int data) {( E2 ^$ U4 O w8 i
Node x = get(index);
$ D) r- k. c+ [6 f- P9 c int oldVal = x.data;9 H; i& M% a2 y# @! |
x.data = data;# w& U; J. |0 `) ]
return oldVal;+ p" h/ E O/ T! {! N5 A; p5 X& `
}
0 I' b, D6 g; | }% x% N4 ^# f/ N4 ~
3. 插入节点只要内存空间允许,能够插入链表的元素是无穷无尽的,不需要像数组那样考虑扩容的问题。 与数组类似,链表插入节点时,同样分为3种情况。 - 尾部插入
- 头部插入
- 中间插入 d, S, o' J8 }. c' o
3.1. 尾部插入尾部插入,是最简单的情况,把最后一个节点的next指针指向新插入的节点即可。
) U7 I! `" u) }9 b8 e4 X& j' b
# y0 O h$ ]- y9 Z# T
3.2. 头部插入头部插入,可以分成两个步骤。 - 第1步,把新节点的next指针指向原先的头节点。
- 第2步,把新节点变为链表的头节点。
5 R1 t6 q: [+ Q; k
7 g! _2 o- s# \7 q: a' W) y
0 a: L/ T0 k6 k* R! C# H3.3. 中间插入中间插入,同样分为两个步骤。 - 第1步,新节点的next指针,指向插入位置的节点。
- 第2步,插入位置前置节点的next指针,指向新节点。7 [( a# B0 E& H$ Q
7 O: g+ t! Z- I9 P
, x7 P, l0 S9 n' s$ ]0 Y三钟情况的代码合到一起" p9 N2 C) b- Q) A% c( D& q6 x8 m
; A& f! @$ i2 m% y& ]& {1 o8 n+ r/**
8 p8 f& t" S4 w/ M. A; g * 链表插入元素
# M. D9 @7 `: i5 F *
4 ]3 y( q8 [" w& w9 w2 }: B7 v; { * @param index 插入位置/ P* T) U% c: i: t
* @param data 插入元素 被插入的链表节点的数据9 k! |- R8 Y8 g/ H
*/
2 O6 }3 z) D- ?, I$ T- M public void insert(int index, int data) {/ Z7 O, g3 c* w. Y4 w0 V0 z
if (index < 0 || index > size) {
7 M; u- [0 Y# M throw new IndexOutOfBoundsException("超出链表节点范围!");
$ Q" b1 i+ y* v W7 F% B' S9 X, c }
. S$ |5 B+ o; K, \0 x$ j- p1 z Node insertedNode = new Node(data);
1 H6 ^1 F. `! @1 ?9 b- I, }1 q4 a if (size == 0) {
& z/ C! q" P& Z% ?6 L //空链表9 K, p( z) H5 J3 [! ^
head = insertedNode;2 Z, r* j9 Q- y
last = insertedNode;7 H1 _! T' z8 r7 T
} else if (index == 0) {$ Z) R3 f7 g$ H6 t( [ f
//插入头部7 h7 G+ v- j/ ?* S( K5 b
insertedNode.next = head;% k3 S: A. N8 a; p* S; F2 o" G; b I4 w
head = insertedNode;4 S+ g5 z6 g1 c* {
} else if (size == index) {, S: O8 {- J, b, Z2 E$ y
//插入尾部
5 E6 k: V, v3 z7 ^# V last.next = insertedNode;5 ?: n/ |# Y- H* p g% K
last = insertedNode;% i) P8 |# H0 w. v0 A- P( ]
} else {) y& l g R, y& m8 N3 d" `
//插入中间. Q5 i' F8 J4 A
Node prvNode = get(index - 1);
. e6 K+ h) K2 f ~' Y5 | insertedNode.next = prvNode.next;
7 Q7 K" S0 F, M prvNode.next = insertedNode;9 a' R# v6 O, V) m4 u
}5 [9 B0 g8 s& Q5 s
size++;
* J' P* c0 l" K O5 y! _/ }1 z* @ }- w ^7 K5 K' o; {
! ^9 Z# q! J5 q% t$ o3 ?/**. @2 w7 H# M# l% a( D4 V
* 链表插入元素
5 U5 a: r$ g' n9 e3 F *
5 ]( l6 d1 h) O. a2 V1 Z1 @ * @param index 插入位置
+ S7 o! y6 \7 E3 c * @param data 插入元素 被插入的链表节点的数据
& f3 m7 n `* G& J, e: ^+ C */
$ E ~9 a4 i' w8 ? public void insert(int index, int data) {7 y! u* ? U1 f1 r2 h6 U
if (index < 0 || index > size) {, |' \4 v1 [) |
throw new IndexOutOfBoundsException("超出链表节点范围!");
4 W3 O6 C' w8 t+ g# \ }
' N3 ] @' G4 }' R c Node insertedNode = new Node(data);; I. \* f7 b- ^' F% H
if (size == 0) {
9 A: q: x/ b& i3 B //空链表: ^+ s* a- {) @' o: j7 @# U$ k
head = insertedNode;1 p7 {! q" H% {& J
last = insertedNode;
9 L5 m) `- |1 ~6 f. ?7 h! t' ^ } else if (index == 0) {# w5 q2 G: ^2 p u
//插入头部
8 L7 E# f4 ~# V( u& z- n8 J insertedNode.next = head;
! S5 ^! Y0 n5 Z head = insertedNode;8 I+ m8 }0 l1 R6 W3 d1 N
} else if (size == index) {3 t1 @1 u O5 ^8 q0 O
//插入尾部6 d, y' d' Q. L+ A; _# F0 \
last.next = insertedNode;
; o5 H. p8 L W6 s+ p7 N5 R& P last = insertedNode;
. {1 W# s1 L' h: d( L" _ } else {1 |( ^5 V3 n8 `* ]. d& j
//插入中间
/ ]" U' `7 G) V7 K+ A' J Node prvNode = get(index - 1);
( \2 d, O, [4 d! K6 e; B insertedNode.next = prvNode.next;
3 o% T" ^3 ~7 Q r2 x prvNode.next = insertedNode;
, _9 I- c3 c& s, m' _ }
* I! a9 u8 r" q! J3 P' L size++;8 r1 d; y2 g$ t
}4 w: Z! x& Z( O3 m! r2 w6 M1 s* p
4. 删除元素链表的删除操作同样分为3种情况。 - 尾部删除
- 头部删除
- 中间删除
" L- |8 H& B/ L1 V& w t l, i$ o! M 4.1. 尾部删除尾部删除,是最简单的情况,把倒数第2个节点的next指针指向空即
: X# s$ V- t! ~+ @, q可。
1 g i% w0 z8 r, G
4 f- s( J" j7 h3 l9 i
4.1. 头部删除头部删除,也很简单,把链表的头节点设为原先头节点的next指针即可。
0 c2 K; R/ p& d, g7 J; a$ ]% ` i0 a @* Y; i) l8 ^6 ^3 B
4.1. 中间删除中间删除,同样很简单,把要删除节点的前置节点的next指针,指向要
$ ?: F7 E. I8 K% C4 O5 h1 J; Z& f删除元素的下一个节点即可。
1 m- t/ ]* w2 W, Y) h8 E! c. D* w* g7 O' p
" k) W5 p8 ^) I这里需要注意的是,许多高级语言,如Java,拥有自动化的垃圾回收机制,所以我们不用刻意去释放被删除的节点,只要没有外部引用指向它们,被删除的节点会被自动回收。; r* m+ y! J3 P$ C6 J
如果不考虑插入、删除操作之前查找元素的过程,只考虑纯粹的插入和删除操作,时间复杂度都是O(1)' a: N" I; {* z& B8 ]* [$ \5 x
/**
' ]- h: L# D1 g# j" { * 链表删除元素( Z9 Q0 C; a3 q6 F8 o
*$ T7 S3 N4 u' b7 o6 k! r, L. E# o6 Q
* @param index 删除的位置9 o9 ]. |, O0 u
* @return 被删除的节点
! h# q1 r- v3 |1 M */
# [8 P6 |+ v @) m public Node remove(int index) {" l2 V' Z) y1 c6 T# x8 C
if (index < 0 || index > size) {
7 _& Y- s3 n3 U throw new IndexOutOfBoundsException("超出链表节点范围");6 G- D1 u+ q: m
}
4 s0 ^5 r9 u. K1 i1 {" @ Node removeNode;. s6 D. U5 I. M# p, K
if (index == 0) {
( F1 g1 a& F& V3 M& J, t if (size == 0) {5 t; _; r- p0 c# H+ p$ P; q
throw new NullPointerException("当前链表为空,不可以进行删除操作");- @" f1 d+ k/ F1 s5 V
}7 }( |1 c# s+ J& \/ X$ i! P1 {" `
//删除头节点
* w X8 s: }8 U {, e( C removeNode = head;
9 Y L" y* T* b' y+ w head = head.next;4 b% d" f9 f( d2 m- N0 B
} else if (index == size - 1) {9 K6 X) X! Q d; e) _: I
//删除尾节点' J) f# @* u; }
Node preNode = get(index - 1);
# R: m5 U. P1 O removeNode = preNode.next;, P$ D. V j3 n" [5 n! g; Q
preNode.next = null;
8 ?: U8 T( ?' t3 {! w0 u( A last = preNode;1 ~$ d) M- {8 @; p6 |: t' [
} else {
/ f+ l. c* F1 ?( z //删除中间节点
# Z1 z3 U' s- T6 C* j Node prevNode = get(index - 1);
/ V% d& s6 j+ I2 J9 { removeNode = prevNode.next;
" Q/ H# v! I+ l1 V6 d4 m' V- H prevNode.next = prevNode.next.next;4 e: c6 t* u& }
}
0 [; l$ C9 {3 r1 Y8 c size--;" _7 j# \! c* J5 P4 |" W6 I- D
return removeNode;8 F' Q5 Y1 V3 Q" X
}4 s% \! r& s# |$ v
Java实现链表的完整代码package chapter2.part2;
+ I3 G1 M, K$ ] ~
) {2 N2 s# o. ^# \3 h) P/**' w5 f$ ?( W2 q% z8 `) t! V% w4 I* P% {
* Created by IntelliJ IDEA.
6 b. [5 F. D# A, R+ j *6 }0 L) }' l7 I" P
* @Author: 张志浩 Zhang Zhihao, w7 ?0 ]# u% j- s" c. N$ Q5 D1 K
* @Email: 3382885270@qq.com1 |2 J( p( N. l1 ~
* @Date: 2020/5/37 O) F4 p; U$ K6 {& v: P7 V' h& x
* @Time: 13:39. `5 i2 j6 i3 m9 n
* @Version: 1.0
9 A* O3 ]( x: u2 \ */3 L$ G w/ O1 |$ ]
public class MyLinkedList2 {
2 S1 H; O: P# P4 A+ Q: t6 U; @ private Node head; //头节点& o; c% s% L3 d5 @
private Node last; //尾节点# y" I' e' M `1 |* l7 ?# V
private int size; //链表实际长度+ |5 {# c9 b7 c# T
* u1 V3 b7 ?6 B5 U E6 e public static void main(String[] args) {- w3 _" U9 Q6 D' N) j9 d
MyLinkedList2 myLinkedList = new MyLinkedList2();
- |2 c, t# r+ c: H// myLinkedList.remove(0); // java.lang.NullPointerException: 当前链表为空,不可以进行删除操作! J9 k: c8 R# I' @: Q6 j8 v
// myLinkedList.remove(3); // java.lang.IndexOutOfBoundsException: 超出链表节点范围% g. F4 p2 V4 `6 ~2 l
myLinkedList.insert(0, 3);* F2 P% |( ?* b4 k
myLinkedList.insert(1, 7);+ W q- M, n* x6 M
myLinkedList.insert(2, 9);# ]2 Q; D; n" ~- [
myLinkedList.insert(3, 5);' u/ H, m7 U3 O4 E( ], E/ C
myLinkedList.insert(1, 6);
2 V( U5 k0 i' J6 c2 G# b2 w! U& J/ O myLinkedList.remove(0);4 r9 w, p# C' x4 q9 ^( v) v
myLinkedList.set(0, 23);( n! H; m: ?' x) p% Z
myLinkedList.output();
+ l8 M# @2 c" ~6 B4 v }
W2 n; d4 y8 D) ]8 [( V' e: d7 y$ y- u k x: r7 r5 Z g: x' ]3 s2 c; o
/**
" U2 }3 j: F& u9 Y& [& C * 链表插入元素
8 [7 v7 Q X. h3 P; D *
3 `6 j- e( x7 U( C * @param index 插入位置 w3 ]% M$ l! _/ H
* @param data 插入元素 被插入的链表节点的数据$ @; u1 i6 X5 e& ~4 E8 `
*/
, [7 ?' U* M; ^# w) c) M# Y1 G public void insert(int index, int data) {* ^1 K- `9 G4 V
if (index < 0 || index > size) {; I* k8 e- H' j# |/ s
throw new IndexOutOfBoundsException("超出链表节点范围!");
, B5 P+ A1 H. G- A+ c5 { }1 ?1 f* ^/ M7 C( `+ h, g
Node insertedNode = new Node(data);+ j0 |, a C. x# M
if (size == 0) {
5 V( v0 S6 n/ x' E% Z. z //空链表8 ?8 o6 L6 C: k( r M& t
head = insertedNode;
8 x! }+ D T0 l' z' p) p last = insertedNode;1 q" [. F y8 M( g' K2 g$ b
} else if (index == 0) {
0 r) W, a1 M" f& J. `) N: ^. a2 L //插入头部; U7 I3 y: C% e
insertedNode.next = head;9 [" V3 L- l' D: O( n
head = insertedNode;
% c+ L( m- ~8 P, @- P } else if (size == index) {- V; S6 `9 ~+ O; V5 w
//插入尾部
: T( N* C. P/ B) B8 x& L! ^/ G# N9 T8 b last.next = insertedNode;2 b% z2 O" J1 s$ J4 \
last = insertedNode;# j {7 P8 d' C( U
} else {
- w: J; N% m2 c3 t: E% C //插入中间
1 k8 S s% a1 I$ S0 X+ {2 k Node prvNode = get(index - 1);2 A0 }4 G. ~+ j9 {0 R
insertedNode.next = prvNode.next;+ M1 _3 F, C$ u
prvNode.next = insertedNode;
. F& V+ H* G% b: W { }
A2 A: q3 l- p+ L$ l; p2 P size++;
0 g" ~2 T1 |! n+ {5 z }
$ Z6 U& P& I) A. ^5 `! x7 p* g" c- W/ m* u; r; V* k2 k: |
/**
! {! i1 G9 v6 G * 链表删除元素
/ R+ H* S' x- K3 o3 n& K *
# @8 A* c$ Q6 E' F+ G$ ? * @param index 删除的位置
" p# Q4 k3 Y- D$ M, K R. t& [+ U1 X * @return 被删除的节点
2 W% v3 i4 U+ x& } */ e" J7 F U% j7 q2 l/ M( m
public Node remove(int index) {
5 x0 |* T" o) u" z0 T1 w; G if (index < 0 || index > size) {
0 S' Y! u; `1 h throw new IndexOutOfBoundsException("超出链表节点范围");( V' z0 n- N8 q: K' b
}$ @% N$ H1 q8 G% l! v0 k
Node removeNode;
1 \, I2 ^ j- u9 n) { if (index == 0) {
1 }* L) _- X% J4 `$ A% [ d if (size == 0) {
2 Q) a* s' I/ J- y4 W/ K7 g throw new NullPointerException("当前链表为空,不可以进行删除操作");! C. @5 e$ a3 E" G& r' w* W
}( y& R7 v4 b' {1 y" `0 M% C- C: u
//删除头节点( A; v9 ?4 \/ v3 o
removeNode = head;
- i, k& J2 z( [3 p head = head.next;
! q" S/ H l5 c* R- } } else if (index == size - 1) {2 i8 g" `: o* I9 H6 Y
//删除尾节点3 Y$ }; ~3 Z3 H0 @/ O8 Z% m
Node preNode = get(index - 1);- f! x2 A$ R$ C2 h, ]
removeNode = preNode.next;( i6 a$ |7 ]% L7 ?, h3 m
preNode.next = null;* q4 F. Q3 z5 v& R4 G
last = preNode;1 `. n) D: V' j6 X
} else {' T+ j) B- U6 ~3 Q% b; U3 n. u9 x
//删除中间节点
: n, u( s/ x0 N7 q! I5 P Node prevNode = get(index - 1);* [& B& S- H5 E2 |8 w5 O8 p% d. L
removeNode = prevNode.next;0 l7 J( C1 d- L/ c) K* t$ m6 m; o H0 k
prevNode.next = prevNode.next.next;
0 [4 f3 R- L9 M5 f" }/ o }
1 \9 I. [# R8 e+ g& O0 ^1 S8 A4 Q, Y- K size--;! X" M1 P8 q& ^& L
return removeNode;' T5 p, W( O" R* ?" U
}; [, s: w* c5 u8 t' ?0 i
$ h: d- z0 W3 e* e+ K /**( Y( m$ _1 r7 _3 t& k5 T6 ?) j
* 更新节点 将列表中指定位置的节点的data替换为指定的data。
) q+ U8 Y, X0 i* i( J *6 d2 Q' ]; [% \5 [+ [. K
* @param index 需要更新的节点的位置
% Y4 l% [8 T5 y v: Q. \' p * @param data 新data
3 y: o5 e! Y/ g3 t2 T * @return 旧data/ {8 s" t2 P0 B3 q: d, J
*/* o2 I K9 c9 D
public int set(int index, int data) {. a% D) S" d' |% y G
Node x = get(index);
; R9 d# t$ N- w1 c& }, c int oldVal = x.data;" R: T: _4 q' N
x.data = data;5 R; e. C: R2 O$ z' H
return oldVal;
3 Y- w2 u5 ]5 l2 q9 q& R! h W }9 Y' T }" @0 ?/ @ W" ^" K" d/ T
9 ]+ E ]: I+ A7 p9 ^0 C /**9 W" O, b' w6 u/ b) D, w
* 链表查找元素
" V% r& e6 W$ ^, l+ M6 G+ {2 Y *' y' [& t/ y9 Q: j1 j' s! U: Q7 `( T6 \8 f1 d
* @param index 查找的位置
" g7 @4 x* R( z g! j9 L7 }$ B1 O * @return index位置的Node对象
8 s/ n% ~: z7 @2 _! T. ?2 k */' o2 `* v9 y( s0 |9 O9 N5 t5 _6 X* K
public Node get(int index) {
+ }" ~. E! k n5 s; ?; X if (index < 0 || index > size) {
g( h* B3 O; }# d throw new IndexOutOfBoundsException("超出链表的节点的范围!");! M; d! W+ y* G( b
}
; j5 N2 o5 D# I+ Q$ ~: b0 V, ? Node temp = head;/ A+ G) F+ u1 T" i K; }3 r
for (int i = 0; i < index; i++) {7 E W( P- Y! P- x+ {9 f1 {
temp = temp.next;: ]" N/ e9 |& o) Z& G3 B
}4 k+ g/ O- J" q) h- u4 K
return temp;
: d% O9 d% a' Z9 q' T) A" G }
& @- J. ^% g- W; l- ?- n: `7 D; \* f5 [
/**
1 z9 n6 }* ~ C * 输出链表
4 h+ ^6 d* y* \- Q0 e */
6 Q9 b; e1 X! n' f, U4 b public void output() {
( g6 k& x! u; Z! W5 [ s' W Node temp = head;
7 }+ x6 D* ]5 o5 Z& z9 P) O while (temp != null) {9 F+ x- @9 V, f: y8 n- l& w6 n6 v
System.out.print(temp.data + " ");
" U4 T4 A: x3 `2 H temp = temp.next;
+ r% [" }. e* `/ g }
; J' w4 j; ^# J6 m6 f5 V2 V }" G) j3 `0 ^' d8 N: y3 V8 t
6 h5 P: x! q% D0 @, f- q /**
. q q) s* g# R: h0 h8 r/ W- | * 链表节点6 W* V. m+ @. ]6 ~7 m8 Q9 N* ^
*/
3 E* Z/ a: }. X9 y. ~ class Node {, D! R5 O5 p4 J4 {* c9 i' [
int data;9 S7 h2 n& O1 i& m+ N
Node next;
/ Z9 L3 J9 ]- q" S5 ]- E) n* B$ n- N& C, o8 Z" E; U
Node(int data) {
. L5 d5 D# a1 R$ c, l$ k( N this.data = data;
5 S$ [, H( K% p6 T5 R }5 F. p2 X, L- z
}
2 Z" S: X/ R2 e+ K}! R& [8 z, v* A x1 @
+ f0 B% D1 \: ? D
) Y# O8 l- {0 u. p9 j9 F4 G* s二、双向链表
; b0 x( [3 A5 Y. J& _0 z+ b
双向链表比单向链表稍微复杂一些,它的每一个节点除了拥有data和next指针,还拥有指向前置节点的prev 指针。3 T# w2 ]8 } g
. E6 K7 X! ~1 }
, c# S5 ]' I8 [5 p! d
) a5 \6 }7 A$ t. M# k+ p- A. y3 S- H( F' w( m9 C
————————————————+ v9 S H o' V, ^; O
版权声明:本文为CSDN博主「爱做梦的鱼」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。/ V. U. Z! @/ Q
原文链接:https://blog.csdn.net/weixin_43124279/article/details/1059044685 s; k2 U( Q2 P) }5 _1 `3 F- `
9 {6 v% G& k* W
/ J! Z1 y' M. Y" r6 w. @9 M |