1 x' A, }/ [9 w9 t' j; c1 [
【Java演示】什么是链表?数据结构
2 P7 Q0 Y& e+ ]9 ~& M |一、单向链表3 {* k) W' r8 f) Y Q+ z7 j; B
; a Y+ d. c/ A; s4 Z9 Z+ z7 `8 {
& Z, z7 `% ?- i' J z; L2 T& p
链表(linked list)是一种在物理上非连续、非顺序的数据结构,由若干节点(node)所组成。
0 Y3 F5 \ T9 {1 J8 Q L( A. z1 l单向 链表的每一个节点又包含两部分,一部分是存放数据的变量data,另一部分是指向下一个节点的指针next。4 @) |- R6 j% }2 W. R! w# i* s6 |
链表的第1个节点被称为头节点,最后1个节点被称为尾节点,尾节点的next指针指向空。
# ?/ Q/ y* B: Y/ O: L) c: Q8 N8 y9 v' x" w% D- c
什么叫随机存储呢? K" r; F) L9 R) e5 V: L4 X/ {
3 u H) V# e0 h6 ], p2 G
如果说数组在内存中的存储方式是顺序存储,那么链表在内存中的存储方式则是随机存储 。
& z3 [1 D, E' r$ V+ A上一节我们讲解了数组的内存分配方式,数组在内存中占用了连续完整的存储空间。而链表则采用了见缝插针的方式,链表的每一个节点分布在内存的不同位置,依靠next指针关联起来。这样可以灵活有效地利用零散的碎片空间。
' t3 S1 _0 ] |$ l6 ^$ n9 w: m
3 m3 _- _, h: f3 W+ F) \9 S/ _5 U2 L8 d! c9 m/ d5 f8 y( X3 T& _; \+ B; s8 \& \
图中的箭头代表链表节点的next指针。 链表的基本操作1. 查找节点在查找元素时,链表不像数组那样可以通过下标快速进行定位,只能从头节点开始向后一个一个节点逐一查找。
# ]* h& I8 J) E
' s! ]: ~7 e* @/ h/**4 i9 Q+ Q9 v) A, ~7 t
* 链表查找元素
" G) m0 _2 N# h4 u3 d. U *
, m2 g M U6 { * @param index 查找的位置7 ]/ T3 m$ e7 v- `- D1 U
* @return index位置的Node对象
0 ]- @7 W% H; Z9 T+ i! h3 X, w3 o% k */# n; ]5 R, a1 {) `! E- |( `3 F
public Node get(int index) {
* G' _# v, l: u- \+ U" ] Y9 X- }9 z5 L if (index < 0 || index > size) {* F8 D3 Y/ Y& D
throw new IndexOutOfBoundsException("超出链表的节点的范围!");6 |1 ]+ `# s! e' e! B5 _
}- s5 V) p( J* @
Node temp = head;6 F9 t7 g3 g* Q" \) [+ j
for (int i = 0; i < index; i++) {; I8 F8 g' n) F! _6 e+ ]$ j0 k
temp = temp.next;
; S, F, y/ o4 b* D) O* m2 Y }1 @ v% e& |$ W F
return temp;4 w* D, e! X. u4 g
}
F5 M r6 z7 E3 {' ~
) {, B5 @2 h% m% ~, F& v链表中的数据只能按顺序进行访问,最坏的时间复杂度是O(n) 2. 更新节点
; t" q# v# n8 u0 N% X2 q3 F
3 W; E+ G* P, K% [* d4 s2 _
如果不考虑查找节点的过程,链表的更新过程会像数组那样简单,直接把旧数据替换成新数据即可。
/ @. i& R8 K. \0 F如果不考虑查找元素的过程,只考虑纯粹的更新节点操作,时间复杂度是O(1)2 t( B9 R+ E, G5 b: I8 q# r+ o
/**
( @+ |) X9 i2 }3 H8 r" k) } * 更新节点 将列表中指定位置的节点的data替换为指定的data。/ L$ Z) c G( S
*# \+ Y2 [" m' Y s; \5 n
* @param index 需要更新的节点的位置) s1 m% e2 t5 A+ q+ Z7 m( d0 m
* @param data 新data
' K: D t& Z4 z, ^ a * @return 旧data
5 z. g( ~3 q$ |$ y$ y */
( m+ Q+ s7 `" |( G public int set(int index, int data) {: j. A" `; O' d% \, z# i3 ?# e. w
Node x = get(index);
; h5 `8 c, V: z4 o int oldVal = x.data;
; O# M) ]+ ]5 b+ h% S8 P& A0 e7 V' D x.data = data;/ Q- H! b/ R) I' j' V; C
return oldVal;
0 k. e: p5 t9 X" E- S3 G- N }& L1 D% y4 f8 y( X/ U
5 L5 v' B: r: L% M; W' U# |3. 插入节点只要内存空间允许,能够插入链表的元素是无穷无尽的,不需要像数组那样考虑扩容的问题。 与数组类似,链表插入节点时,同样分为3种情况。 - 尾部插入
- 头部插入
- 中间插入
/ G) e& ^0 H9 e% R" a+ N 3.1. 尾部插入尾部插入,是最简单的情况,把最后一个节点的next指针指向新插入的节点即可。 - \4 F+ g: i- V k- f L4 _
. i9 f4 v4 h1 [+ {2 Q! C) J3.2. 头部插入头部插入,可以分成两个步骤。 - 第1步,把新节点的next指针指向原先的头节点。
- 第2步,把新节点变为链表的头节点。
J( Z1 @% C u" L- d
3 Q( q f8 I: A3 {4 T1 t
6 R" s0 q$ ~/ t1 j! Q' n3.3. 中间插入中间插入,同样分为两个步骤。 - 第1步,新节点的next指针,指向插入位置的节点。
- 第2步,插入位置前置节点的next指针,指向新节点。
2 G9 y( l& L5 v: U! P0 Z
- ]# R4 W7 |/ g$ k
2 ?! w# X& |+ s1 w) @$ I8 q) n三钟情况的代码合到一起
7 p% H3 G1 p$ Y4 {0 \" S1 S% h* g4 d2 w8 ]; Q2 E8 @
/**; X9 t) d, T* S
* 链表插入元素( }! ^' S5 F% i& A3 `. j( r' J* k1 S
*
! r4 t8 ~2 D0 L3 X& x * @param index 插入位置0 Y" M2 q# B6 H+ ^6 P
* @param data 插入元素 被插入的链表节点的数据2 e$ f% y9 l3 F' N# o1 n- B
*/
% G: A5 g' E% U- _ [0 @5 E/ ]: b public void insert(int index, int data) {2 i$ W; H6 U0 B0 }$ y( v
if (index < 0 || index > size) {
! Q) f( f: x# L( T throw new IndexOutOfBoundsException("超出链表节点范围!");/ D; ]; g6 G5 g0 `1 g( J9 t" @0 Q0 C
}! a. t. ?) ^' [: e8 m: h. H# k
Node insertedNode = new Node(data);
7 a5 Z, z; F2 t4 B# H9 ^ if (size == 0) {
( Z$ G+ t* K/ i# y //空链表
6 ~, }) V# H; K# g- K head = insertedNode;3 C9 Z& V+ n" p& ^$ ~
last = insertedNode;6 T+ @9 k8 j7 ^5 G* O; ~
} else if (index == 0) {
' g% t. u8 X* L //插入头部
3 k) o. x, t! u$ X& p! \3 T insertedNode.next = head;
. z' h/ w6 B* T; a- t O head = insertedNode;
9 p1 b+ |9 E7 l. j6 ~3 T } else if (size == index) {
: o$ q/ `. Z% A& V* s+ ` //插入尾部
8 w7 L4 S \; r: C% u n9 |: s last.next = insertedNode;
1 r; f+ a% X) U% v) r last = insertedNode;
0 V2 Z( b' y! U; W6 E9 [0 ` X* F } else {
0 [. E7 \- m8 A6 v //插入中间
; ^& X" c# U$ H% I Node prvNode = get(index - 1);' N+ E, h3 J9 N- {8 W
insertedNode.next = prvNode.next;
2 F2 d& T! J9 M" k/ B H5 H prvNode.next = insertedNode;# }7 _" A+ d0 { X' n9 z
}' ?) w2 T- A. O, Z8 [1 \( V
size++;
. s9 ?3 [( D# p, ^/ o& ~. m4 _ }$ d* F& d. f, ?. V9 A, n- L2 u
6 N2 E) l' U! W# r6 t
/**
6 a2 K0 _ P" U. A0 H/ S P8 Y! T6 s+ u * 链表插入元素
1 B0 Z2 L* P7 W7 Z; _+ | *
; I4 j' B! O7 e7 f- U" Z# a * @param index 插入位置/ F2 x3 C# `6 L; \1 a& K
* @param data 插入元素 被插入的链表节点的数据 M% B+ c. g0 p9 ^
*/# K3 x( D0 j+ ?4 x$ A* C
public void insert(int index, int data) {
5 d/ }' w$ O' \& B# J if (index < 0 || index > size) {; \5 L1 b) e& L9 E1 ~7 q
throw new IndexOutOfBoundsException("超出链表节点范围!");
# P; Q8 {7 o7 ~' j) |% b- l/ j' m }
b5 a: n3 m; K$ ~ Node insertedNode = new Node(data);" ]& ?' }( J5 Y4 G3 i1 ?/ ]8 q- W
if (size == 0) {
2 {# Y3 s4 T! D //空链表
3 _) _% ^. T. A) ]' {7 T2 R head = insertedNode;: ]1 Z7 W% w6 W7 x
last = insertedNode;1 c8 v3 x1 r; T! p8 e
} else if (index == 0) {- @6 I+ Y! h( z# b M5 n
//插入头部
1 C% b! {- G4 [0 }5 Y insertedNode.next = head;+ X/ v* D( v2 o* _3 q
head = insertedNode;
) E5 P# w; Z1 \" g } else if (size == index) {
: H& N. J: Y2 G9 A1 E3 g //插入尾部
4 ], Z' l, x) [ y3 F last.next = insertedNode;* P5 Z9 Q7 p: B. c2 \/ R& d8 B
last = insertedNode;
3 H4 y* i) }& Y( \- T1 g3 e } else {
w. z, f' Z& W //插入中间1 W8 N) j$ e+ [
Node prvNode = get(index - 1);% a4 K" Y( r& r4 e4 x: n# o
insertedNode.next = prvNode.next;
( \) a+ T* g3 I8 f prvNode.next = insertedNode;
+ w; [2 [# H! H. x- C% v/ D }
5 f& `6 O: [. ^1 l% k4 s1 o. W8 o size++;
* k4 K/ `9 p) c) O% i1 V } }4 n8 w; u) x8 i' x6 b+ X8 h
4. 删除元素链表的删除操作同样分为3种情况。 - 尾部删除
- 头部删除
- 中间删除( e" d5 |5 E! A! S& n
4.1. 尾部删除尾部删除,是最简单的情况,把倒数第2个节点的next指针指向空即8 Q7 I: P( v. `
可。
( E# E! Y7 N. P1 [
/ W/ c% v2 e5 `- p. n- D* H0 W( ?4.1. 头部删除头部删除,也很简单,把链表的头节点设为原先头节点的next指针即可。
* {. X! `6 J2 Y
/ L* S3 ?2 O3 R- d, A* n8 ^4.1. 中间删除中间删除,同样很简单,把要删除节点的前置节点的next指针,指向要" Y" [+ H) V. P5 y
删除元素的下一个节点即可。
5 c! \' J7 p- W: \9 B' n+ Q
2 i/ A; D" Z+ y5 M, {, s
2 B) K% |. ^5 E9 H2 M
这里需要注意的是,许多高级语言,如Java,拥有自动化的垃圾回收机制,所以我们不用刻意去释放被删除的节点,只要没有外部引用指向它们,被删除的节点会被自动回收。4 {& B( U, F3 L+ W6 B
如果不考虑插入、删除操作之前查找元素的过程,只考虑纯粹的插入和删除操作,时间复杂度都是O(1)8 ?# j" x" q, D# i$ q7 k
/**- s9 H/ N3 n, h( }5 x7 h& t+ F; e
* 链表删除元素
' G* x- k7 X# v$ |8 w( l/ { *
! B$ f4 K' j7 D" G l * @param index 删除的位置
# M# O9 \, `; X P$ k4 r( H0 |$ q * @return 被删除的节点
' C6 ]# u3 Q1 q. d */
' I% u2 u; F% z1 @' } public Node remove(int index) {
4 M a t& n" x if (index < 0 || index > size) {
5 q9 |' w4 \& ~& m4 n throw new IndexOutOfBoundsException("超出链表节点范围");) b- t" `* X3 K1 k) n
}
+ {7 D* I) ~, g" h* n Node removeNode;
, R8 v& H! w/ e0 t% G4 a3 g if (index == 0) {. F3 T' w6 ^4 ]- N8 o8 w
if (size == 0) {
9 i' C0 _, M% G+ s; x throw new NullPointerException("当前链表为空,不可以进行删除操作");
6 k4 a9 p6 u8 k: \% C( P; U( C }
1 B- ]0 j$ P& L+ m6 @4 g //删除头节点
9 t1 X; Z: o' L* l removeNode = head;
( @/ Q g0 S: a: N" k: U. H' f head = head.next;8 y) D8 Y' V, X7 p
} else if (index == size - 1) {" k2 h4 Z8 V3 E' o
//删除尾节点
4 O- ?0 |& P8 c7 \/ u. Q e) _ Node preNode = get(index - 1);
7 T& R( p! Z8 a5 m# B removeNode = preNode.next;2 I. b6 M9 W4 y1 p+ w; B
preNode.next = null;; {0 _$ P2 K) g `
last = preNode;" S$ t* i* g9 y9 W$ K. F8 F& c( S$ C
} else {, r# A2 C/ S- ]: N( T; u
//删除中间节点
' {' }& v/ s- j L Node prevNode = get(index - 1);$ G) R: ~* l! T: U- E2 g
removeNode = prevNode.next;) Q3 t8 d+ G. r
prevNode.next = prevNode.next.next;: M) R- e- z; n% `1 d# H
}3 S) N+ E8 `8 } p
size--;- P3 S/ i8 C/ _
return removeNode;
& R% W0 C0 S+ S$ W9 I6 K: q9 l }
e- I: U! q( h+ TJava实现链表的完整代码package chapter2.part2;0 V! R: u5 E% I9 ^' V5 k$ B6 G' D' W% V3 c
) N7 j+ R5 B9 m; L% p0 x) j
/**
; a. `; q4 b- ]8 n6 v/ b * Created by IntelliJ IDEA.: h) }" Y' t, Y |7 a
*& W/ D" Q5 Z- L7 k
* @Author: 张志浩 Zhang Zhihao! ]$ H9 ^& I" |( ^: E
* @Email: 3382885270@qq.com9 w% h$ a* ]8 B2 a c- E) g3 s- B
* @Date: 2020/5/3/ |# {# u3 m* Q; ]+ V" L& {- r
* @Time: 13:39
* |- z2 o# K: C$ S2 c+ j * @Version: 1.0$ q- D4 y$ X. H* s1 r
*/: ]) r# m" D4 d. {" \( v" t& Y
public class MyLinkedList2 {
0 n! n% j1 X2 g+ q, @" v! ] private Node head; //头节点
$ i+ |8 w$ P# a- O& A private Node last; //尾节点
, s4 U: S+ W7 X private int size; //链表实际长度
+ _9 {3 ]7 X2 w, R0 z8 V( i2 x
( `) L8 c: t: i) }$ x public static void main(String[] args) {% L% n4 P- g( C+ C2 ?! F6 b
MyLinkedList2 myLinkedList = new MyLinkedList2();
* p0 t( M' R7 L; p// myLinkedList.remove(0); // java.lang.NullPointerException: 当前链表为空,不可以进行删除操作; e p& u m8 `" t Z
// myLinkedList.remove(3); // java.lang.IndexOutOfBoundsException: 超出链表节点范围
; n8 s- r: e$ |; K9 E1 w myLinkedList.insert(0, 3); i. g7 Q5 F, f7 ?+ X8 q
myLinkedList.insert(1, 7);
e1 z$ ^1 |# u: a, Y G myLinkedList.insert(2, 9);# ]* A$ d$ h7 {( {
myLinkedList.insert(3, 5);/ {( S6 j0 t8 i; u) ]$ H5 {
myLinkedList.insert(1, 6);
5 Y) J; r1 \& v' d7 ^ myLinkedList.remove(0);
- U+ E" Q; T2 X myLinkedList.set(0, 23);
4 C, e: S% P9 u! p, y6 P myLinkedList.output();
% e; g) M3 H" f5 ? }- w- r- L* o3 P F
3 s4 n) b. c& A /**
2 Q, L9 r5 L" \) }+ Q * 链表插入元素+ S4 @6 i/ ~ j, C! k$ B% F( u. F
*
5 W* `: ] P' s% m * @param index 插入位置
: X2 B* f2 p' Y* e- I& g$ M9 ? * @param data 插入元素 被插入的链表节点的数据
; X' s- S/ d6 `& z* ` */
8 N; u+ ^# B x6 L public void insert(int index, int data) {
7 {' T' c2 Q: v' |3 i8 m if (index < 0 || index > size) {* D: M9 a" ?& E$ _$ n/ P, w
throw new IndexOutOfBoundsException("超出链表节点范围!");. }2 b$ x' j; _/ J0 d+ C( S, d, C
}% ^! x0 L% U% a+ m' k+ \5 ]# {
Node insertedNode = new Node(data);) g; d$ ?7 X6 P8 M: t/ ^
if (size == 0) {0 k Q" o& C& v; l0 ~- |( p# y- `
//空链表
8 z$ N5 K- J+ z5 {: g3 c2 @0 l head = insertedNode;
+ f, i5 [6 m& V last = insertedNode;( A3 L4 X1 P+ O/ h( D/ t9 C
} else if (index == 0) {
. p: T2 J/ G3 u5 K3 X, r //插入头部. S9 g, V2 `/ |3 a6 J
insertedNode.next = head;' b" Y/ `' V. O$ u" i4 M+ I
head = insertedNode;, p# V9 i( g: D; W
} else if (size == index) {1 L4 q/ L; j) d& M& g) y2 Y
//插入尾部
* c' S. w! ~8 u# ]0 Q last.next = insertedNode;
/ Y/ S. j* p4 Y last = insertedNode;4 t5 B$ J2 O+ u3 W" R3 E5 s
} else {
+ o1 T& F1 z o //插入中间
2 ?/ O5 M+ q, A Node prvNode = get(index - 1);' e2 ~1 l7 k2 b7 {
insertedNode.next = prvNode.next;
_8 _8 O& N9 b3 m/ ?; S; d prvNode.next = insertedNode;+ ^* g7 F& C' j5 m' Z) [8 m$ ?7 ^) @
}
# ^4 s2 L" O7 M- u2 @7 t' D size++;
/ J3 Z5 w' t: |8 F/ R3 v6 R$ g }
" W8 B* |$ l1 |
: h' g: x+ L4 ?/ T2 f; ^! ~ /**4 p! }& k/ ^1 ]7 U
* 链表删除元素% L- L( q; C( S7 X0 N7 R% d
*
; [6 y# T4 J. ~8 X4 B' q% U. J * @param index 删除的位置
. c, E, s7 f% z9 c: \ * @return 被删除的节点! ^* w! V7 l* P+ n# V
*/
# Q9 ]9 z! c) @8 D- T i9 B0 e public Node remove(int index) {
/ [% W8 Y6 ~$ {2 f) e* } if (index < 0 || index > size) {
. U# y( g( A" Z7 X2 W P throw new IndexOutOfBoundsException("超出链表节点范围");# r& e- z C% O' }
}5 M* l# \1 E9 t* V; r2 d
Node removeNode;
" }' S2 [; F3 h' x5 \( D; [ if (index == 0) {) b! c9 V# c O# N) k# g
if (size == 0) {
: M6 k; I9 J! n+ x/ M2 X& H% L throw new NullPointerException("当前链表为空,不可以进行删除操作");
$ e' [. W) C6 E }% z+ q' D3 Z$ Q' Y, L W
//删除头节点& E# d7 i# p$ {6 m: I* w% D* \
removeNode = head;" S$ R* K* W9 p4 Y9 y% Z8 P1 ^
head = head.next;
' I, t+ c$ Q0 T$ {+ F3 L$ s } else if (index == size - 1) {
* q) Z5 v2 [; D' Y* S; I! \ //删除尾节点) G$ ?2 M" c: }+ j
Node preNode = get(index - 1);
# L" M& P' n( O removeNode = preNode.next;& }( l t. {2 H( q. ~4 \/ V
preNode.next = null;
" v$ S2 @- l" k4 v& ?2 O$ r2 K last = preNode;' y) r3 m/ w4 d9 r
} else {
$ A/ w5 [5 ]6 P5 Z9 t" ^ //删除中间节点- b9 t2 H7 g' e: N
Node prevNode = get(index - 1);
0 W) c% I& a9 | ]% P# { removeNode = prevNode.next;
. }2 @# N$ p8 D7 B) f7 p2 E, p, }0 W prevNode.next = prevNode.next.next;+ r4 d( f. \ J7 E
}# ^( P% Z3 u" K: n
size--;
! g/ ]% W) J' D' { return removeNode;
2 T' V0 p% z' ]( W) j% _ }
/ z+ Y' y8 Q% m; M9 E% b
" j5 f/ t' _' p9 n. q% l /**' t( {- u7 A" ^# F5 h2 S J
* 更新节点 将列表中指定位置的节点的data替换为指定的data。
- S u9 s' }4 y, T *6 O4 G+ m; l% H# P! c
* @param index 需要更新的节点的位置
+ b) o: E2 S5 {6 A% T * @param data 新data
8 Y' d `$ z/ x3 R* | * @return 旧data
9 R/ w; z+ @# F% l */1 ^/ f* T& O a% U8 F' T1 l7 Z
public int set(int index, int data) {4 ~4 Z; k: _1 R3 V9 B
Node x = get(index);4 N: H- m* }$ `+ |3 ~" X5 k: @
int oldVal = x.data;
5 z0 M& T9 ~4 F5 n/ B x.data = data;
3 m- e2 k0 R0 h# \2 v2 { return oldVal;5 f9 @1 z6 b1 o1 a4 |4 J" ~ W5 p
}
z3 Z F C0 `. r% N, M. m5 `. P) h" t6 p/ U! ]6 r- k
/**$ p' U0 a0 h& }" y2 M/ m
* 链表查找元素" k8 d+ D5 X0 g+ ], D
*2 x0 x8 B9 W) L( Q
* @param index 查找的位置
2 N. Y6 l" k, v2 ~$ T * @return index位置的Node对象3 T/ ^ C& d9 |2 q
*/
( t, c4 `$ }( T4 x% _- } k public Node get(int index) {
" P* Z4 O0 j! u+ c; J/ K" M1 `0 [ if (index < 0 || index > size) {: e' b0 a9 r: k% p8 @
throw new IndexOutOfBoundsException("超出链表的节点的范围!");7 l0 ]- c7 Q: `2 ]0 [
}/ Z+ @; k# M4 s/ u
Node temp = head;
" }! I9 x7 I+ T" Q for (int i = 0; i < index; i++) {
' p- f K% v3 T9 v0 w0 O temp = temp.next;
9 E- ^) y* X+ ?( W! { }4 m% g% ?. Z( e9 t5 R3 x ]
return temp;
* X4 w" ~: r9 j% M5 x2 d }
: A/ h% j+ \: X0 E4 |
$ m( s+ i2 L: ^, @3 J5 T: e /**
; R3 e2 M" `. @. w* h! C* k * 输出链表
9 E- C* I, ]2 ?* m F */
8 Q9 e7 ~ ?# i: C public void output() {0 s L- b. d. o& j( \
Node temp = head;6 B7 o+ g- t' m9 ~ |% f3 W" J- c% i: @
while (temp != null) {
: D! G6 K- s; W System.out.print(temp.data + " ");
- ]& K$ [: E3 a6 Y temp = temp.next;1 k9 L" o1 {% D" L
}# O1 G( y) t; b1 S8 R5 ]
}
2 T) `' N9 a0 W/ ~; i, v+ e( X* X. ]
/**0 ]' X8 G; |! [6 L9 o
* 链表节点
1 d- o5 c7 u" `- b */
9 H7 T; P0 k3 B9 Y8 z class Node {5 A, E& {4 F0 w+ y0 F7 O( j5 U
int data;5 d" u- Y" o# A& L' M
Node next;4 E: N% g$ |. m) U0 {
6 S2 t0 U: @6 k# W+ _, E0 Z Node(int data) {
2 o4 \# W/ `- L+ g: a Q this.data = data;! }/ q4 ~& I2 [
}0 a v3 y; @. w" [5 I8 c6 v) T* w
}
$ x* E7 v+ F: K" C5 j+ b. k& Z}) V) ?5 F5 d& b
: Q% M9 o4 f6 f* W6 ]8 ~
, Y% N8 o. D( y3 M- a2 ?
二、双向链表
! E6 }( C( T5 S: X2 Z' @
双向链表比单向链表稍微复杂一些,它的每一个节点除了拥有data和next指针,还拥有指向前置节点的prev 指针。. ^5 I6 e: G1 y* G
: c* p3 W: ?% R: u0 D& V' q
# L$ }. x& W2 Q W% g- |7 D$ c
8 b5 @' Q! u2 m E9 h5 d1 ^; L A8 p. Z( ?0 f, B+ z
————————————————5 ^6 d3 T$ A4 i j
版权声明:本文为CSDN博主「爱做梦的鱼」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
& f7 N9 B3 @% Y# k* d' |1 Y原文链接:https://blog.csdn.net/weixin_43124279/article/details/105904468
# b" J# h1 X, O
: V1 c O3 P: h. v
9 z% T: s( X- j |