4 A2 ?% s/ t( ~. P【Java演示】什么是链表?数据结构
9 K& O9 `, T8 f$ y/ i一、单向链表7 f; i! k; e5 P5 N8 Q! S
2 |; q( g. u' M
5 j0 k$ q! K) |# @链表(linked list)是一种在物理上非连续、非顺序的数据结构,由若干节点(node)所组成。
* Y; a- M: h# W, f单向 链表的每一个节点又包含两部分,一部分是存放数据的变量data,另一部分是指向下一个节点的指针next。
- M8 ^( c3 J$ G, K2 p7 G链表的第1个节点被称为头节点,最后1个节点被称为尾节点,尾节点的next指针指向空。' c- J4 U# q1 b# i( G6 Y
6 X& t" V( a2 Q$ \什么叫随机存储呢?
/ F. Q) f: a5 h- a" |8 f" `; Y
4 E* d( ^( O2 t d# B$ ?* [! A如果说数组在内存中的存储方式是顺序存储,那么链表在内存中的存储方式则是随机存储 。/ T& d( E9 v, V6 G
上一节我们讲解了数组的内存分配方式,数组在内存中占用了连续完整的存储空间。而链表则采用了见缝插针的方式,链表的每一个节点分布在内存的不同位置,依靠next指针关联起来。这样可以灵活有效地利用零散的碎片空间。+ c( I- a. O: ]. F6 V1 {+ G& P
4 _8 v v3 S7 s( h) M. N% A$ {
5 W' w% l$ H: m. l" q/ v/ \
图中的箭头代表链表节点的next指针。 链表的基本操作1. 查找节点在查找元素时,链表不像数组那样可以通过下标快速进行定位,只能从头节点开始向后一个一个节点逐一查找。
( F" \% K2 p" a- W) z
' c9 H* G) ^5 G$ o: ]/ [; T
/**1 G" T7 M0 O1 p' d
* 链表查找元素5 ]+ m+ ?, K( Q W
** l6 T5 v' ]; T
* @param index 查找的位置
9 A. G8 J. F9 ] * @return index位置的Node对象) m; Z7 K% T( h( c* D
*/$ `: m, s8 F! G% n1 E5 x* R
public Node get(int index) {1 T4 g% K% \" a
if (index < 0 || index > size) {
# }+ F! q' x, Y7 k, s5 Z2 T6 _4 Y: l' H% D throw new IndexOutOfBoundsException("超出链表的节点的范围!");. Z; I$ |% b& @/ b- H. m+ p
}
: i, `$ z4 F$ c/ e9 o/ Q# L7 s Node temp = head;
6 A: @# B, `- w( k6 c9 y for (int i = 0; i < index; i++) {
8 l0 c' u T6 E, t2 w. { temp = temp.next;
8 V. d8 @. x6 O) C6 b }
( C4 `' K! N6 @8 T' i q# o return temp;
- r3 i: u( \/ o& G* ?: \0 q: J }
# Z- T! G6 b) z* o. H1 C! l) N' G1 q
; f1 m: u1 P# G* z1 s链表中的数据只能按顺序进行访问,最坏的时间复杂度是O(n) 2. 更新节点
: k& H) U+ Z6 {/ W, ~3 M
. C# @6 R% q& T5 O; k, B8 g如果不考虑查找节点的过程,链表的更新过程会像数组那样简单,直接把旧数据替换成新数据即可。# ]' A; {/ s0 K* i+ _5 O" g! |9 c
如果不考虑查找元素的过程,只考虑纯粹的更新节点操作,时间复杂度是O(1)( i2 G8 p7 r9 p$ n- ?
/**; X% L: f0 S( ^! H8 P/ x
* 更新节点 将列表中指定位置的节点的data替换为指定的data。+ N& W, d8 C- h, Q0 X- g3 A9 ` ^7 {
*
% S K d9 _ t5 ^' E * @param index 需要更新的节点的位置
5 y3 ^. a/ e2 k! k# i# t * @param data 新data
4 t- ]8 z+ ]0 u' d * @return 旧data
) d. O% D W4 d. n' x */$ j, m* m& p4 @7 N
public int set(int index, int data) {% t8 y. R) H! u; x" \2 X; h
Node x = get(index);: X8 r8 C: ]- U V& @% E, t8 R
int oldVal = x.data;
; d* v! ?8 l; b! Q5 b8 S' w) g4 c( B x.data = data;
3 W" |4 p J+ o! J return oldVal;
8 s7 T1 W& g5 e# {9 ^$ h }+ k6 i) N# i+ Z' J3 ~' N
1 |+ t& m1 \ G
3. 插入节点只要内存空间允许,能够插入链表的元素是无穷无尽的,不需要像数组那样考虑扩容的问题。 与数组类似,链表插入节点时,同样分为3种情况。 - 尾部插入
- 头部插入
- 中间插入! N- g6 x) L: h/ _& T2 }8 h; W% S' n; \
3.1. 尾部插入尾部插入,是最简单的情况,把最后一个节点的next指针指向新插入的节点即可。
/ {0 k2 d& w% ~( Y1 O+ S- j9 \
- n, {- D: h+ v: A9 W4 D$ V5 ?( ~
3.2. 头部插入头部插入,可以分成两个步骤。 - 第1步,把新节点的next指针指向原先的头节点。
- 第2步,把新节点变为链表的头节点。0 a m( V; {9 @% y. z+ R
4 V7 ]4 E L$ P( C9 |: B; W. J/ ^ ?$ f- t7 H
3.3. 中间插入中间插入,同样分为两个步骤。 - 第1步,新节点的next指针,指向插入位置的节点。
- 第2步,插入位置前置节点的next指针,指向新节点。
; B' S2 R3 S+ R4 _* U- W8 F/ Y4 f
: R7 K/ s% c6 g) ]
. s# K. j' c0 e5 C三钟情况的代码合到一起$ g! Q& b9 P+ z4 x( Z% Z) `
: ~8 ^, H6 o1 A4 V4 B/*** ]+ [$ _; L6 w2 M1 j
* 链表插入元素. o$ h# ]' H3 K* u, |" s% \6 l
*# n1 w) E" r; W7 ]0 h, V8 S8 E
* @param index 插入位置
: C3 v" K. j/ D* G * @param data 插入元素 被插入的链表节点的数据- a- `. @" w- T7 P/ V* J
*/
. Z$ Z) B# V' n8 Q. w& I' t& H3 S public void insert(int index, int data) {
$ Z6 ^* |5 i- f6 z/ f+ j if (index < 0 || index > size) {7 E0 E5 m0 j6 P) X5 O
throw new IndexOutOfBoundsException("超出链表节点范围!");' \7 q8 \. `3 }* M0 C5 u
}
8 f" e& I7 b4 N, O$ c7 y Node insertedNode = new Node(data);! J9 q7 h" |, m2 j; Z
if (size == 0) {9 S# ^9 }9 q* ]6 v# P( q3 a
//空链表
/ E6 q, T/ }* P C& m( N6 [ head = insertedNode;
! b% m0 A: n7 g5 e, ~9 m# w last = insertedNode;; v5 v2 y7 N9 n7 q. B$ k1 y
} else if (index == 0) {
' q0 G3 G' N5 a0 ~' V. Y! g4 g$ w //插入头部4 q& t# x+ m2 z& v
insertedNode.next = head;
, B; t2 X4 v. P- s0 H head = insertedNode;+ E7 D; O1 ]( T8 ~
} else if (size == index) {
6 e2 u+ l k& J //插入尾部% u( h0 m% d) h9 {! a' D" l, a
last.next = insertedNode;1 Y& |+ ~/ V; ^$ Y. R: E
last = insertedNode;
& y0 [! K7 f/ a# | } else {
' x: I1 f5 C# \, b( r6 P. m% D //插入中间
2 ?% G6 H. r2 |8 X Node prvNode = get(index - 1);3 `! D" e3 v. Y
insertedNode.next = prvNode.next;1 r! ?- E# @- R" _5 a' M, [
prvNode.next = insertedNode;/ F9 S3 s" E0 }1 R& w: P# z; z
}: b3 X" R0 ] \) J! Z1 X
size++;6 m: O' c+ B8 S3 z! a# M" Y' p" \
}
$ T% s8 i6 `3 s& o/ W6 n4 {9 v
3 C7 @6 i/ N2 o. c% p8 s/**
* m/ `: b) U$ u5 r8 @' a9 ] * 链表插入元素
t6 t6 l1 `' g+ j *
* s) l: i$ S; Z" K7 i: s3 W * @param index 插入位置6 L, J$ w/ h N
* @param data 插入元素 被插入的链表节点的数据5 }' ~' F' G- p; X: E
*/
9 R' C# `7 `# d# b; O' C( P0 q' e public void insert(int index, int data) {( V+ q' d4 @$ |& t0 a9 @
if (index < 0 || index > size) {6 ?6 R* P% ]3 S% [' y
throw new IndexOutOfBoundsException("超出链表节点范围!");3 r/ o9 p7 \7 w) @
}- d) l9 N( D: x* j. p
Node insertedNode = new Node(data);9 V# X, x: {7 ?0 B/ i
if (size == 0) {
9 e1 \ {3 a8 t2 Z/ Q- k5 k6 | //空链表
* f: p, M+ }2 a- v1 O head = insertedNode;3 ^) Q3 d/ S2 k) |: b/ n
last = insertedNode;
3 e* _2 L8 H' z# x } else if (index == 0) {
& _) ?1 i ]( \8 X" y5 i! z. A //插入头部5 V9 r8 n) h% Z, f0 W7 H* U% E
insertedNode.next = head;
! j1 v. J O6 u, | head = insertedNode;
% Q4 q$ N9 a. ?( q1 M5 h } else if (size == index) {; g3 s0 I( `) ~( z$ W1 a
//插入尾部
5 a( e8 Y1 n- k. \& j last.next = insertedNode;
) H; d( z$ I- V0 V& I6 Y/ e last = insertedNode;
' W6 v0 g6 [) Q% R; M } else {
# C/ o. T+ h7 ~+ k4 p //插入中间" L5 L I r# c _+ c% f
Node prvNode = get(index - 1);
* P: u9 v" S$ n" _7 q8 } insertedNode.next = prvNode.next;7 o) d2 g3 H4 r' N7 p9 m1 Y
prvNode.next = insertedNode;8 m8 Q( f4 I8 |3 \
}7 u( S0 {3 H* b6 R
size++;' Y/ f/ ]6 z: Q' @% j% \6 w3 j& R
}( }5 ^; {/ ]( z* u7 O
4. 删除元素链表的删除操作同样分为3种情况。 - 尾部删除
- 头部删除
- 中间删除
) S# \5 s: Q& z! ] 4.1. 尾部删除尾部删除,是最简单的情况,把倒数第2个节点的next指针指向空即- i4 A3 q4 k8 G, W0 E) C% `& |: g- I
可。
9 k4 }8 C9 n% ?- L+ H7 j* ]# I
: K; v7 V) \7 f7 X* D# u. r5 Q4.1. 头部删除头部删除,也很简单,把链表的头节点设为原先头节点的next指针即可。
1 D3 f, H `& ]9 R' L# l
7 ?. o* V" W: {/ g$ h4.1. 中间删除中间删除,同样很简单,把要删除节点的前置节点的next指针,指向要
, R; ^6 @8 X1 n+ M: ~" Z删除元素的下一个节点即可。
J' {& [& |* L, h" T$ ^; `9 }, i0 e
7 W6 ]2 \, q) j; H# b. _4 q! Q' E' n8 k3 [9 P- C/ g' |
这里需要注意的是,许多高级语言,如Java,拥有自动化的垃圾回收机制,所以我们不用刻意去释放被删除的节点,只要没有外部引用指向它们,被删除的节点会被自动回收。3 t9 {! @8 h2 g0 j3 g
如果不考虑插入、删除操作之前查找元素的过程,只考虑纯粹的插入和删除操作,时间复杂度都是O(1) P: |7 T* y; U0 O
/**
: T) o' \6 }; i H3 y * 链表删除元素
8 l; ?; u/ u4 P- q2 x# b; | *
+ q& A p, v# i" t9 F( G/ l) g * @param index 删除的位置/ q- v* X. {, s7 k4 t7 f
* @return 被删除的节点
8 l+ T. Z' v. [- Y: | */
: D; _0 N+ U& O- T0 q4 X public Node remove(int index) {9 z4 ]6 R( l; e5 u( Y/ A8 I" ?4 b3 I
if (index < 0 || index > size) {2 B e# L2 G% {- Q
throw new IndexOutOfBoundsException("超出链表节点范围");1 F0 c( J+ D9 x
}" a# E7 u7 K; h* \
Node removeNode;
3 e& |% w5 ^' F1 A5 u* Q if (index == 0) {$ I2 V2 e2 J8 k1 B6 `1 q- w
if (size == 0) {5 W, h+ Z( S( o! d) B8 f2 D2 r
throw new NullPointerException("当前链表为空,不可以进行删除操作");
# W7 w' Y" r4 [! W. R& P }
6 e5 X* o8 c, ] //删除头节点, ]3 {" Y2 p: G9 O3 ]4 \- {
removeNode = head;4 s$ B, S/ v: h3 h
head = head.next;
, |2 {; s4 [; n; `; k/ a, P" n; x } else if (index == size - 1) {6 s( i- @/ M1 T3 k) q6 e
//删除尾节点
" l: x& [3 {( Y2 d Z8 a Node preNode = get(index - 1);' L3 h- k# H; X* x
removeNode = preNode.next;
5 M4 H" [( N+ z8 S$ @7 A5 s preNode.next = null;
3 l, ~1 U+ A! m) d last = preNode;
' j. N7 B, Q& ? } else {
# k0 x# r. F* W& h4 K- g8 ` //删除中间节点
7 p2 f- \3 Z& ?0 R Node prevNode = get(index - 1);/ r/ X' X8 V; n3 ~" q( e3 @; {
removeNode = prevNode.next;$ J/ t B2 ?3 p7 e9 ~0 X) E
prevNode.next = prevNode.next.next;8 i' i2 u4 S( e( }0 b. ~
}5 M( _1 q5 R+ k) C+ Q) h
size--;
# `- ?2 G# N# ~1 k! @6 b return removeNode;
2 D8 `% p U8 o' ]! f2 P }5 }2 i! v9 C' [. t ^7 ]* ^
Java实现链表的完整代码package chapter2.part2;/ y: o( h; v7 u* K; s9 ^
; _3 C# [; D* e* d/ M+ F) p2 _' z" g! q
/*** c5 `; @4 |5 T$ ]- h
* Created by IntelliJ IDEA.
9 w! s( `) e+ y* o2 k4 z *
1 F9 e: D# J- Z Z5 @5 O * @Author: 张志浩 Zhang Zhihao
" j+ f: z) |# P# r * @Email: 3382885270@qq.com
m/ E2 [: v$ s6 O * @Date: 2020/5/3, R1 N2 `! g6 ?' d
* @Time: 13:394 _+ Y- E% G4 B
* @Version: 1.0; l7 }; x) U6 c) D
*/( Y0 w7 Y+ M) c* C5 G" [
public class MyLinkedList2 {& H' |" Z; F; b& X
private Node head; //头节点
" ~. _* R* _; Z# E3 z7 ?$ o; O private Node last; //尾节点
; W! L& f8 {. c0 p3 S private int size; //链表实际长度
7 [) i% ?7 \$ E" _1 \- y
; g$ h- C4 p2 l public static void main(String[] args) {
# l& ?% d. l9 G- z MyLinkedList2 myLinkedList = new MyLinkedList2();- a5 t2 x1 u, y
// myLinkedList.remove(0); // java.lang.NullPointerException: 当前链表为空,不可以进行删除操作* U5 ~1 a. u& j$ n- Z
// myLinkedList.remove(3); // java.lang.IndexOutOfBoundsException: 超出链表节点范围
, q2 X: F# D, ?# k$ V3 z myLinkedList.insert(0, 3);
! {; L0 i8 Q% A! H! W) r myLinkedList.insert(1, 7);
1 F6 m( s _1 a" Z; N. i myLinkedList.insert(2, 9);" J5 R) X5 @0 q2 a. K, X' W
myLinkedList.insert(3, 5);
$ V9 ^0 s# N6 `6 H' }) } ` myLinkedList.insert(1, 6);
5 ]3 z. @) @9 p$ n0 K8 {! C myLinkedList.remove(0);
! Y: k% M( Y5 W0 B myLinkedList.set(0, 23);/ D9 K% j/ o B' _& v1 W: P8 L+ o
myLinkedList.output();
2 ~5 j9 W5 Z6 ^) l% l } Y8 d. |7 B& z
4 Z8 ]- X" f7 g9 @/ o2 F
/**6 S9 _7 x0 c# A3 X
* 链表插入元素
' S$ Z& k9 y7 W *3 B% u. @) U( J' l+ }! V {! m- u
* @param index 插入位置4 D" y* m6 U) u. ?) D; }4 o5 i
* @param data 插入元素 被插入的链表节点的数据
+ G9 b, W9 S8 l; |' Z7 m */
G1 w! a& E$ U9 A6 U+ W public void insert(int index, int data) {
( U! l; g2 m: i; d8 W& W if (index < 0 || index > size) {
0 M9 Y+ I1 G3 ?- z( w1 } throw new IndexOutOfBoundsException("超出链表节点范围!");
! D; e2 g( E/ l }
$ \; l# Y% G5 K( D# S Node insertedNode = new Node(data);
: A2 T' @+ q. S& |7 }5 T' u! Y if (size == 0) {/ v' d! ~$ A% p: p$ o! [2 S7 V
//空链表
$ }9 h0 w; @8 E4 n head = insertedNode;4 M- ^5 |# e9 p/ k; g, w1 q" p& \
last = insertedNode;0 \1 I- T# E$ m: @. d- U. X
} else if (index == 0) {+ W$ a; X! s; B& D) o+ @- A; [
//插入头部
, s u& w* N! I% A! w5 E7 } insertedNode.next = head;
! x" l2 [- ~7 t% G head = insertedNode;
) ^7 K( w1 D1 p7 D- { } else if (size == index) {% ^) W# M( U, c9 i) ~
//插入尾部
0 J o) p- O2 g& E: [ last.next = insertedNode;2 v1 _: Y" O" q' k' \9 s: h; R
last = insertedNode;
# |/ g0 K/ ^% y6 ?5 C } else {" F* w a$ u2 M. A* d$ `& |, e4 i
//插入中间4 k Q% {4 _! J- l
Node prvNode = get(index - 1);
* v$ C+ F( O& i& j3 q; }1 b K insertedNode.next = prvNode.next;- b* @ F4 S- g/ w% {
prvNode.next = insertedNode;
8 b# x+ O( s% Z/ g5 G6 \ }
' ]: z, g6 n, |% H2 t/ D size++;
2 C! C/ u$ m8 l, ] u' p }( \; `! R2 G+ n9 a% p9 ~
# c2 q n0 {; r+ o- O) |) W /**" {) W+ p( e0 P: f7 D, s4 b s
* 链表删除元素
$ p' \4 ^" }7 s8 ~0 i' x *
) N( p& d5 A T * @param index 删除的位置
; i. s; e9 K+ w" i * @return 被删除的节点2 J" V( G! Z. Z/ r2 n4 t9 f
*/* b* K: `+ Q* W1 g7 W
public Node remove(int index) {$ S! x0 V0 H' ]+ V8 I- m3 @9 H
if (index < 0 || index > size) {& b$ F3 v P C8 p' ?) W
throw new IndexOutOfBoundsException("超出链表节点范围");' J0 {' c& W% e: s4 j
}% D& m* a$ u# r
Node removeNode;
* o( d% R' B+ T& j( s, \2 g3 a if (index == 0) {6 L. Q$ _. l' ^- A0 M
if (size == 0) {8 [: O6 h) o0 X0 B+ O" G1 h. b
throw new NullPointerException("当前链表为空,不可以进行删除操作");7 S9 R* i. Z9 T! G5 O q) b
}
) |) G0 u4 J. ?- i: H! k //删除头节点6 A3 x) U2 F% G& ~- H. {, f
removeNode = head;1 e' k0 t+ S5 b0 ^' U
head = head.next;% m8 x& i- G9 |3 a3 H7 A4 x
} else if (index == size - 1) {
5 E* [" p' S* p: F) Y6 X //删除尾节点, H) c3 ]( O- x# a
Node preNode = get(index - 1);
3 {( b% G: G8 M removeNode = preNode.next;
+ F/ q4 g# S! X; h preNode.next = null;
2 m6 h/ [" J/ I6 @3 z$ X' Z last = preNode;
9 _2 J' d* e& T6 B1 C! h } else {
( T( g7 V' n4 U4 |/ T* m //删除中间节点- W+ m9 O& o" n o4 o- |, m/ Y
Node prevNode = get(index - 1);
5 q# @! `' Y, ~5 n. d, c9 a G removeNode = prevNode.next;: b7 h+ l; H( R4 \7 l7 }
prevNode.next = prevNode.next.next;
9 ^% o' |7 A; |7 c) e% J, c }
8 d; Z; A$ ]/ s; \, n: l: d/ } size--;; H6 L+ ?4 N( A/ a- ?4 Z
return removeNode;; n8 N( X& g$ t: _8 m! n
}
; _8 {: @2 o! G4 y
$ O4 L2 m/ I% `: R5 G j /**+ r$ H: Z* T g7 p" W! t- l
* 更新节点 将列表中指定位置的节点的data替换为指定的data。
$ F# N2 R! z8 l" i6 d9 p% o- j# J */ B4 g# ?! {, p J+ s
* @param index 需要更新的节点的位置7 H$ V8 o+ K2 C1 ]% e( ?5 Y
* @param data 新data6 A' \7 J9 M( _$ X9 ^/ H- i( H+ X
* @return 旧data
; a& T v; }8 [8 Y/ L- n [1 C */- N) g$ U( [) L. t5 h y" [
public int set(int index, int data) {
8 i' M( e. i4 E$ A2 C6 w Node x = get(index);" X* v# P. D, J# P, L7 c
int oldVal = x.data;! x0 @, N9 V7 p! a' q2 E
x.data = data;. u- C/ b+ W' ~, [
return oldVal;+ R% m( e, P k) Z0 X E
}4 p+ E) J4 n# | ^, `) ]/ `
4 U/ E A& N6 @ /**
3 M5 b5 r+ m+ ], ^ * 链表查找元素
. p) }: z2 w0 W5 l, ? *
8 t) P4 }1 y; [$ h% n `2 G9 K2 V * @param index 查找的位置
; I+ i- I% @3 M * @return index位置的Node对象% o$ f/ l& O7 N$ R
*/4 \0 D" G# \7 M2 a' [9 e
public Node get(int index) {
( c! u/ w4 e8 j2 z/ x if (index < 0 || index > size) {4 y! Y% |# ]8 t0 R% f5 u
throw new IndexOutOfBoundsException("超出链表的节点的范围!");; q4 P% s) O$ N# u2 Z
}
8 G; b4 A* x/ N7 `5 c1 n8 W Node temp = head;' a* W8 z2 h4 F5 l' i# |& K4 H4 A6 s
for (int i = 0; i < index; i++) {$ t9 @% F& M, @! d5 k% ~8 [( G
temp = temp.next;0 b9 J p" a7 j: p) I
}
- J/ D0 I2 N; \" N* A" s/ |, X2 n- j6 c return temp;
# N! p9 z6 i z, {: `7 H }
( P3 [& ]: \6 A3 q; ~* s: }7 b% W- q) _2 `- \/ M8 q# I
/**
$ F/ F% ?/ A* q4 Q+ ]8 w. K * 输出链表. i5 \& P. t* a4 b; T& ?% s
*/
8 @$ ?$ D9 ^! {) }, S public void output() {
& z* _- {) o$ P' C5 _+ `2 m Node temp = head;/ {+ x/ U+ C0 ?* {7 W! Y
while (temp != null) {
: |6 G( W \, V: g System.out.print(temp.data + " ");% m1 p# |( j- b# K3 A7 W, k
temp = temp.next;
: s5 D. U" P5 h5 i! \ }3 E/ |5 f2 y+ E5 O- z9 K
} L |1 P' g$ x1 Q. T: O5 V5 y. @) D0 t
1 W" |5 [2 F- N6 X /**
' p6 i# ^2 o' S+ T+ L; q * 链表节点
8 w0 H4 l( T3 H$ w */
. r4 S$ D3 V( U" p6 b9 V5 F class Node {$ f) ^% s0 `; f* k0 |; O2 t6 q2 ~
int data;: S. B) X9 [. |- Y* |# }+ t
Node next;% t E( q; p5 P0 X7 K
! l$ @3 h7 D5 d. _: d
Node(int data) {
2 f3 ]& C) v$ k% p8 Q. F- n this.data = data;
) I- e7 ^7 u% A: `# S' T+ u }
' j1 c7 i+ s6 G2 b. ?% g }
( A' v/ \ `4 L( H0 Q% j( ]1 c; c0 C}
! q7 o r i. I P6 ]
! W6 h, I1 I9 Y$ k- @& q1 w- Y7 x- X8 j! ~, y' W- z! w% I
二、双向链表
3 T0 {% G" ?& y& @
双向链表比单向链表稍微复杂一些,它的每一个节点除了拥有data和next指针,还拥有指向前置节点的prev 指针。
8 T2 A/ z4 H' t8 m. Q
! P1 K' s+ A/ o0 V: {. k. |/ k2 h, W# F# k7 j5 u- m
$ h( P5 P; b( [4 @# ?' c/ i/ \6 D3 I! w5 k- [# ^- z
————————————————
/ Z" V; ^4 u2 k) F5 a2 L5 v! C版权声明:本文为CSDN博主「爱做梦的鱼」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
4 r1 }0 u- L/ W2 @3 G5 `8 G- Y; c/ |原文链接:https://blog.csdn.net/weixin_43124279/article/details/105904468# u" X* W; E: `# s7 X! x4 ^
/ [7 G5 u( `7 u8 e
2 O0 Z( E7 ?" A+ [* ^ |