java 线性表和链表_数据结构知识(java版)- 4. 线性表之链表

1. 什么是链表

1.1 链表的定义

链表是一种物理存储单元上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的。

如下图所示,假设要存储线性表a={1, 2, 3},如果通过链表的方式存储则可以不考虑:1)物理空间是否连续;2)元素之间是否顺序。而是通过【指针】的方式来显式指定各个元素之间的相对位置关系即可。

c1143056ffb6bfbf3c9226bbc0d6f5da.png

1.2 链表特性

链表中的数据元素在内存中的存储位置是非连续、非顺序的。

1)优点:相比顺序存储结构,链表的增删改更加灵活,只需要更改当前数据元素附近的数据及指针即可,时间复杂度位O(1);对空间的利用更加灵活;

2)缺点:查询慢,每个数据元素的位置,都需要从链表起始位置开始遍历,时间复杂度O(n);空间利用率低,容易造成存储碎片化;需要额外的指针来指明相对位置,空间成本较高。

1.3 链表基本结构(以单向链表为例)

1)节点、数据域&指针域

首先,链表实现数据元素在物理存储中随机,同时元素间又保持逻辑位置关系的秘诀,在于链表中每个数据元素附带了指针。具体而言,链表中的每个数据元素被设计成一个节点,每个节点由数据域和指针域构成,如下图(单向链表的节点)所示:

数据域(data):用于存储数据元素本身的数据信息

指针域(next):用于存储该数据元素与其他数据元素之间的逻辑位置关系(例如下图中,单向链表的节点的数据域用于指向其下一个数据元素的位置)

e4d105653cfaafd733f72a20e6e091df.png

2)链式结构

我们以线性表的单向链式存储结构为例,说明链表中元素的位置结构。如下图所示:

头节点:一般情况下链表会设置一个头节点作为起始节点,其数据域是null。头节点不是必须的,它的作用只是为了方便解决某些实际问题;

首元节点:第一个存储了真实数据的节点;

其他节点:除头节点和首元节点以外的节点。

尾节点的指针指向null。

06adf064f2916521f25a9a203458898b.png

1.4 链表分类

我们这里只考虑线性表的链式存储结构,可以分为以下几种类型:

单向链表、双向链表、单向循环链表、双向循环链表。

8b5e694ddae29269743fcecf865f5547.png

2. 单向链表实现

这里以单向链表为例,进行java代码实现,双向及循环链表将在后面的文章中学习和介绍。

2.1 初始化

要点:

1)设计节点类

2)初始化的时候需要先new一个数据域为空的头节点

1 public classMLinkList {2 /**

3 * 节点4 */

5 public class Node{6 /*数据域*/

7 publicE data;8 /*单向链表的指针域指向下一个节点*/

9 publicNode next;10

11 publicNode(E data) {12 this.data =data;13 }14 }15

16 /*头节点*/

17 privateNode head;18

19 /**

20 * 初始化单向链表21 */

22 publicMLinkList() {23 head = new Node(null); //头节点数据域为空

24 }25 }

2.2 基本操作

2.2.1 增加数据元素

操作如下,时间复杂度为O(1)

6929584086e07735bb56851dc10e355f.png

2.2.2 删除数据元素

操作如下,时间复杂度为O(1)

c76d7f46dccc168271c95efdcf8ca2f3.png

2.2.3 修改数据元素

只需要修改数据域的值即可,时间复杂度为O(1)

2.2.4 查询数据元素

需要从起始节点开始遍历链表,时间复杂度为O(n)

2.3 完整代码

2.3.1 完整java代码实现

1 packagecom.lrspace.learn.ds.list;2

3 /**

4 * Author: llx5 * Description: 单向链表(首元节点的下标是0)6 * Date: 2021/2/167 */

8 public class MLinkList{9 /**

10 * 节点11 */

12 public class Node{13 /*数据域*/

14 publicE data;15 /*单向链表的指针域指向下一个节点*/

16 publicNode next;17

18 publicNode(E data) {19 this.data =data;20 }21 }22

23 /*头节点*/

24 privateNode head;25 /*当前数据元素个数*/

26 private intsize;27

28 /**

29 * 初始化单向链表(首元节点的下标是0)30 */

31 publicMLinkList() {32 head = new Node(null); //头节点数据域为空

33 size = 0;34 }35

36 /**

37 * 在末尾添加数据元素38 *39 *@paramdata 待添加的数据元素40 */

41 public voidadd(E data) {42 Node tmp = newNode(data);43 Node cur =head;44 while (cur.next != null) {45 cur =cur.next;46 }47 cur.next =tmp;48 size++;49 }50

51 /**

52 * 插入数据元素53 *54 *@parampos 待插入的位置55 *@paramdata 待插入的数据56 */

57 public void insert(intpos, E data) {58 if (pos >= size || pos < 0) {59 throw new IllegalArgumentException("error: the input pos " + pos + " is exceed the bound.");60 }61 Node tmp = newNode(data);62 Node cur =head;63 if (pos == 0) {64 cur =cur.next;65 head.next =tmp;66 tmp.next =cur;67 return;68 }69 for (int i = 0; i < pos; i++) {70 cur =cur.next;71 }72 Node curNext =cur.next;73 cur.next =tmp;74 tmp.next =curNext;75 size++;76 }77

78 /**

79 * 删除数据元素80 *81 *@parampos 待删除数据元素的位置82 */

83 public void delete(intpos) {84 if (pos >= size || pos < 0) {85 throw new IllegalArgumentException("error: the input pos " + pos + " is exceed the bound.");86 }87 if (pos == 0) {88 head.next =head.next.next;89 return;90 }91 Node cur =head;92 for (int i = 0; i < pos; i++) {93 cur =cur.next;94 }95 Node curNext =cur.next;96 cur.next =curNext.next;97 size--;98 }99

100 /**

101 * 更新数据元素102 *103 *@parampos 待更新数据元素的位置104 *@paramdata 待更新数据元素的目标值105 */

106 public void update(intpos, E data) {107 if (pos >= size || pos < 0) {108 throw new IllegalArgumentException("error: the input pos " + pos + " is exceed the bound.");109 }110 Node cur =head;111 for (int i = 0; i < pos + 1; i++) {112 cur =cur.next;113 }114 cur.data =data;115 }116

117 /**

118 * 查询数据元素119 *120 *@parampos 带查询数据元素的位置121 *@return查询出的数据元素122 */

123 public E select(intpos) {124 if (pos >= size || pos < 0) {125 throw new IllegalArgumentException("error: the input pos " + pos + " is exceed the bound.");126 }127 Node cur =head;128 for (int i = 0; i < pos + 1; i++) {129 cur =cur.next;130 }131 return(E) cur.data;132 }133

134 @Override135 publicString toString() {136 StringBuilder out = newStringBuilder();137 out.append("[");138 Node cur =head;139 for (int i = 0; i < size; i++) {140 cur =cur.next;141 out.append(cur.data.toString());142 out.append(", ");143 }144 if (size > 0) {145 out.delete(out.length() - 2, out.length());146 }147 out.append("]");148 returnout.toString();149 }150 }

2.3.2 功能验证

1 packagecom.lrspace.learn.ds.list;2

3 importorg.junit.Test;4

5 import static org.junit.Assert.*;6

7 public classMLinkListTest {8

9 @Test10 public voidtest() {11 /*初始化单向链表*/

12 MLinkList mLinkList = new MLinkList<>();13 System.out.println("单向链表初始化: " +mLinkList.toString());14 /*在末尾添加数据元素*/

15 mLinkList.add("first");16 System.out.println("在末尾添加数据元素1: " +mLinkList.toString());17 mLinkList.add("third");18 System.out.println("在末尾添加数据元素3: " +mLinkList.toString());19 /*插入数据元素*/

20 mLinkList.insert(1, "second");21 System.out.println("插入数据元素: " +mLinkList.toString());22 /*删除数据元素*/

23 mLinkList.delete(1);24 System.out.println("删除数据元素: " +mLinkList.toString());25 /*更新数据元素*/

26 mLinkList.update(1, "second");27 System.out.println("更新数据元素: " +mLinkList.toString());28 /*查询数据元素*/

29 System.out.println("查询数据元素,位置0: " + mLinkList.select(0));30 System.out.println("查询数据元素,位置1: " + mLinkList.select(1));31 }32 }

输出结果:

单向链表初始化: []

在末尾添加数据元素1: [first]

在末尾添加数据元素3: [first, third]

插入数据元素: [first, second, third]

删除数据元素: [first, third]

更新数据元素: [first, second]

查询数据元素,位置0: first

查询数据元素,位置1: second

3. 参考材料

1)百度百科:https://baike.baidu.com/item/%E9%93%BE%E8%A1%A8/9794473?fr=aladdin

2)南孚先生的博客:https://www.cnblogs.com/782687539-nanfu/p/10333031.html

3)解学武的教程:http://data.biancheng.net/view/160.html

原文:https://www.cnblogs.com/llxrl/p/14407804.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值