数据结构系列之链表的数据结构

举报
yd_273762914 发表于 2021/08/05 23:14:44 2021/08/05
【摘要】 数据结构系列之链表的数据结构 在上一章的学习中,我们知道了数组的基本概念和相关特性,接着本博客继续学习数据结构中一个比较常用的数据结构,链表。ps:本博客基于java中的数据结构。 1、什么是链表? 链表是由一系列节点组成的很常见的数据结构,每一个节点都包含一个值和指向下一个节点的指针。“头”节点指向序列的第一个节点,序列的最后一个节点指向NULL(对于单链表)。...

数据结构系列之链表的数据结构

上一章的学习中,我们知道了数组的基本概念和相关特性,接着本博客继续学习数据结构中一个比较常用的数据结构,链表。ps:本博客基于java中的数据结构。

1、什么是链表?

链表是由一系列节点组成的很常见的数据结构,每一个节点都包含一个值和指向下一个节点的指针。“头”节点指向序列的第一个节点,序列的最后一个节点指向NULL(对于单链表)。链表也是线性的顺序存储数据,不过在内存地址上是不连续的。

画图表示单链表:
在这里插入图片描述

2、链表和数组的对比

在上一章节,学习了数组,所以这一章和数组做对比:

  • 数组在内存地址是连续的,与数组不同,链接的节点(链接)不需要连续出现在内存中
  • 链表的大小不需要提前声明,只有内存允许就可以
  • 链表是动态的,所以新增移除节点都是控制指针就可以,所以链表的新增移除操作都是比较快的,这个和数组相反
  • 数组是通过下标指针就可以直接访问节点数据,而链表需要全局遍历,所以数组的访问速度是比较快,链接相反
  • 链表的数据结构需要更多的内存,因为链表不止要保存数据,还要保存下一个节点的指针

3、链表的类型分类

数据结构中支持如下几种类型的链表:

  • 单链表:在单链表中,节点的遍历只能往前遍历,指针从Head节点开始
  • 双向链表:双向链表的遍历导航可以和单链表一样往前遍历,也可以后腿,往后遍历
  • 循环链表:循环链表是一种特别的链表,其最后一个节点的next指针指向第一个节点,也即第一个节点的previous节点为最后一个节点

4、单链表的简介

在这里插入图片描述
由图看出链表的特性:

  • 1、 Head节点指向第1个节点
  • 2、 最后一个节点指针指向null
  • 3、 每一个节点都存储数据和指向下一个节点的指针

5、双向链表简介

画图表示双向链表:
在这里插入图片描述
由图可以归纳双向链表的特性:

  • 1、Head节点指向链表的第一个节点
  • 2、每一个节点都有一个存储数据的节点,和指向下一个节点的next指针、指向上一个节点的prev指针
  • 3、第一个节点的prev指针指向null
  • 4、最好一个节点的next指针指向null

6、循环单向链表

画图表示循环单向链表:
在这里插入图片描述

由图归纳循环单向链表的特性:

  • 1、 Head节点指向第1个节点
  • 2、Tail (Last) 指针指向列表的最后一个节点。
  • 3、每个节点都有存储数据的区域和一个指向下一个节点的指针
  • 4、最后一个节点的next指针指向第一个节点

7、链表的基本操作

本章节以最简单的单链表,介绍链表的基本操作,开发语言基于Java

需要构建一个链表的节点模型,单链表只需要保存数据,加上一个next指针就行:

 private static class Node<T> { public T data; public Node next; Node(T data) { this.data = data; next = null; }
}

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 新增节点
    这种情况,直接将新增节点附加到链表的后面,这里分情况:
    • 1、如果是empty的链表,直接将新节点作为head节点
    • 2、其它节点,直接附加到链表最后面既可
 /**
 * 链表新增节点,直接附加到链表最后面
 * @Date 2021/08/04 13:54
 * @Param [list, data]
 * @return void
 */
public static <T> void add(MySinglyLinkedList<T> list , T data) { Node<T> newNode = new Node<T>(data); // 单链表是empty的,将新增的节点直接作为head节点 if (list.head == null) { list.head = newNode; } else { // 其它情况直接在链表后面附加上新节点 Node<T> currentNode = list.head; while (currentNode.next != null) { currentNode = currentNode.next; } // 将新增的节点作为最后一个节点 currentNode.next = newNode; }
}


  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23

如上的方法,都是固定在链表最后面加上的,然后可以实现在指定节点后面加上节点,这里分三步进行:

  • 1、创建新的节点,赋值数据
  • 2、将新节点的指针指向前驱节点原来的next节点
  • 3、将前驱节点的指针指向新建的节点
/**
 * 在指定前驱节点后面附加新节点 <br>
 * @Date 2021/08/04 14:26
 * @Param prevNode 前驱节点
 * @Param data 新增节点数据
 * @return void
 */
public static <T> void addAfter(Node<T> prevNode , T data) { if (prevNode == null) { System.out.println("前驱节点不能为null"); } // 原本链表0->2,创建新节点1 Node<T> newNode = new Node<T>(data); // 将新节点的指针指向前驱节点原本的next节点 1->2 newNode.next = prevNode.next; // 将前驱节点的next指针指向新节点 0->1 prevNode.next = newNode;
}

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 移除节点
    移除节点,可以在指定位置移除节点,这里可以分为几种情况:
    • 1、移除第一个节点。
    • 2、移除任何中间元素。
    • 3、移除最后一个元素。
/**
 * 指定下标移除链表节点 <br>
 * @Date 2021/08/04 14:34
 * @Param [list, pos]
 * @return void
 */
public static <T> void removeAtPosition(MySinglyLinkedList<T> list , int pos) { // 当前节点 Node<T> currentNode = list.head; // 记录前一个节点 Node<T> prevNode = null; // 下标计数器 int counter = 0; if (currentNode != null) { // 移除第一个节点 if (pos == 0) { // 指针移动,节点往后移,再将下一个节点置为head节点 list.head = currentNode.next; } else { while (currentNode!= null) { // 找到移除节点要节点位置 if (counter == pos) { prevNode.next = currentNode.next; break; } else { // 记录前驱节点 prevNode = currentNode; // 往后遍历 currentNode = currentNode.next; // 记录指针位置 counter++; } } } }

}

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
/**
 * 通过数据找到节点,移除节点 <br>
 * @Date 2021/08/04 15:12
 * @return void
 */
public static <T> void removeByValue(MySinglyLinkedList<T> list , T data) { Node<T> currentNode = list.head; Node<T> prevNode = null; if (currentNode != null) { // 第一个节点 if (currentNode.data == data) { // 节点移动,改变head节点 list.head = currentNode.next; } else { while (currentNode != null && currentNode.data != data) { prevNode = currentNode; currentNode = currentNode.next; } // 找到了节点 if (currentNode != null) { // 节点移动 prevNode.next = currentNode.next; System.out.println(String.format("移除节点:%s" , data)); } else { // 找不到对应节点 System.out.println("遍历不到对应data的节点"); } } }
}

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 链表size
    这个逻辑就比较容易理解了,直接遍历,统计就行
/**
 * 统计单遍历节点数量 <br>
 * @Date 2021/08/04 16:25
 * @Param [list]
 * @return int
 */
public static <T> int size(MySinglyLinkedList<T> list) { int counter = 0; Node<T> currentNode = list.head; while (currentNode.next != null) { currentNode = currentNode.next; counter ++; } return counter;
}

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 链表查询
    变量查找的,可以通过递归,也可以用while循环
/**
 * 查询单链表<br>
 * @Date 2021/08/04 16:24
 * @Param [head, data]
 * @return boolean
 */
public static <T> boolean search(Node<T> head, T data) { if (head == null) { return false; } if (head.data == data) { return true; } // 递归查找 return search(head.next , data);
}

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

8、Java实现链表实例


/**
 * 单链表java代码实现
 * @date 2021/08/04
 */
public class MySinglyLinkedList<T> { public Node<T> head; private static class Node<T> { public T data; public Node next; Node(T data) { this.data = data; next = null; } } /** * 链表新增节点,直接附加到链表最后面 * @Date 2021/08/04 13:54 * @Param [list, data] * @return void */ public static <T> void add(MySinglyLinkedList<T> list , T data) { Node<T> newNode = new Node<T>(data); // 单链表是empty的,将新增的节点直接作为head节点 if (list.head == null) { list.head = newNode; } else { // 其它情况直接在链接后面附加上新节点 Node<T> currentNode = list.head; while (currentNode.next != null) { currentNode = currentNode.next; } // 将新增的节点作为最后一个节点 currentNode.next = newNode; } } /** * 在指定前驱节点后面附加新节点 <br> * @Date 2021/08/04 14:26 * @Param prevNode 前驱节点 * @Param data 新增节点数据 * @return void */ public static <T> void addAfter(Node<T> prevNode , T data) { if (prevNode == null) { System.out.println("前驱节点不能为null"); } // 原本链表0->2,创建新节点1 Node<T> newNode = new Node<T>(data); // 将新节点的指针指向前驱节点原本的next节点 1->2 newNode.next = prevNode.next; // 将前驱节点的next指针指向新节点 0->1 prevNode.next = newNode; } /** * 指定下标移除链表节点 <br> * @Date 2021/08/04 14:34 * @Param [list, pos] * @return void */ public static <T> void removeAtPosition(MySinglyLinkedList<T> list , int pos) { // 当前节点 Node<T> currentNode = list.head; // 记录前一个节点 Node<T> prevNode = null; // 下标计数器 int counter = 0; if (currentNode != null) { // 移除第一个节点 if (pos == 0) { // 指针移动,节点往后移,再将下一个节点置为head节点 list.head = currentNode.next; } else { while (currentNode!= null) { // 找到移除节点要节点位置 if (counter == pos) { prevNode.next = currentNode.next; break; } else { // 记录前驱节点 prevNode = currentNode; // 往后遍历 currentNode = currentNode.next; // 记录指针位置 counter++; } } } } } /** * 通过数据找到节点,移除节点 <br> * @Date 2021/08/04 15:12 * @return void */ public static <T> void removeByValue(MySinglyLinkedList<T> list , T data) { Node<T> currentNode = list.head; Node<T> prevNode = null; if (currentNode != null) { // 第一个节点 if (currentNode.data == data) { // 节点移动,改变head节点 list.head = currentNode.next; } else { while (currentNode != null && currentNode.data != data) { prevNode = currentNode; currentNode = currentNode.next; } // 找到了节点 if (currentNode != null) { // 节点移动 prevNode.next = currentNode.next; System.out.println(String.format("移除节点:%s" , data)); } else { // 找不到对应节点 System.out.println("遍历不到对应data的节点"); } } } } /** * 打印单链表数据 <br> * @Date 2021/08/04 16:24 * @Param [list] * @return void */ public static <T> void printLinkedList(MySinglyLinkedList<T> list) { Node<T> currentNode = list.head; while (currentNode != null) { if (currentNode.next != null) { System.out.print(String.format("%s -> ", currentNode.data)); } else { System.out.print(String.format("%s", currentNode.data)); } currentNode = currentNode.next; } System.out.println(); } /** * 查询单链表<br> * @Date 2021/08/04 16:24 * @Param [head, data] * @return boolean */ public static <T> boolean search(Node<T> head, T data) { if (head == null) { return false; } if (head.data == data) { return true; } // 递归查找 return search(head.next , data); } /** * 统计单遍历节点数量 <br> * @Date 2021/08/04 16:25 * @Param [list] * @return int */ public static <T> int size(MySinglyLinkedList<T> list) { int counter = 0; Node<T> currentNode = list.head; while (currentNode.next != null) { currentNode = currentNode.next; counter ++; } return counter; } public static MySinglyLinkedList<Integer> buildLinedList() { MySinglyLinkedList<Integer> list = new MySinglyLinkedList<Integer>(); MySinglyLinkedList.add(list , 0); MySinglyLinkedList.add(list , 1); MySinglyLinkedList.add(list , 2); MySinglyLinkedList.add(list , 3); MySinglyLinkedList.add(list , 4); MySinglyLinkedList.add(list , 5); MySinglyLinkedList.add(list , 6); MySinglyLinkedList.add(list , 7); MySinglyLinkedList.add(list , 8); MySinglyLinkedList.add(list , 9); MySinglyLinkedList.add(list , 10); return list; } public static void main(String[] args) { MySinglyLinkedList<Integer> list = buildLinedList(); System.out.println(String.format("linked list size : %s" , size(list))); // 打印验证,0 -> 1 -> 2 -> 3 -> 4 -> 5 -> 6 -> 7 -> 8 -> 9 -> 10 printLinkedList(list); // 在head节点的下一个节点后面加上新节点 addAfter(list.head.next , 11); // 打印验证,0 -> 1 -> 11 -> 2 -> 3 -> 4 -> 5 -> 6 -> 7 -> 8 -> 9 -> 10 printLinkedList(list); // 重置链表 list = buildLinedList(); // 在对应位置移除链表节点 removeAtPosition(list , 1); // 打印验证,0 -> 2 -> 3 -> 4 -> 5 -> 6 -> 7 -> 8 -> 9 -> 10 printLinkedList(list); // 重置链表 list = buildLinedList(); // 根据数据移除链表节点 removeByValue(list , 10); // 打印验证 ,0 -> 1 -> 2 -> 3 -> 4 -> 5 -> 6 -> 7 -> 8 -> 9 printLinkedList(list); // 重置链表 list = buildLinedList(); Node headNode = list.head; if (search(headNode , 5)) { System.out.println("查找到对应的节点"); } else { System.out.println("不能查找到对应节点"); } }

}



  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164
  • 165
  • 166
  • 167
  • 168
  • 169
  • 170
  • 171
  • 172
  • 173
  • 174
  • 175
  • 176
  • 177
  • 178
  • 179
  • 180
  • 181
  • 182
  • 183
  • 184
  • 185
  • 186
  • 187
  • 188
  • 189
  • 190
  • 191
  • 192
  • 193
  • 194
  • 195
  • 196
  • 197
  • 198
  • 199
  • 200
  • 201
  • 202
  • 203
  • 204
  • 205
  • 206
  • 207
  • 208
  • 209
  • 210
  • 211
  • 212
  • 213
  • 214
  • 215
  • 216
  • 217
  • 218
  • 219
  • 220
  • 221
  • 222
  • 223
  • 224
  • 225
  • 226
  • 227
  • 228
  • 229
  • 230
  • 231
  • 232
  • 233
  • 234
  • 235
  • 236
  • 237
  • 238
  • 239
  • 240
  • 241
  • 242
  • 243
  • 244

9、参考文档资料

https://www.javadevjournal.com/data-structure/linked-list/

文章来源: smilenicky.blog.csdn.net,作者:smileNicky,版权归原作者所有,如需转载,请联系作者。

原文链接:smilenicky.blog.csdn.net/article/details/119276857

【版权声明】本文为华为云社区用户转载文章,如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。