链表是一种物理存储单元上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的。链表由一系列结点(链表中每一个元素称为结点)组成,结点可以在运行时动态生成。每个结点包括两个部分:一个是存储数据元素的数据域,另一个是存储下一个结点地址的指针域。

单向链表

单链表是一种链式存取的数据结构,用一组地址任意的存储单元存放线性表中的数据元素。链表中的数据是以节点来表示的,每个结点的构成:数据 + 后续元素位置指针。

单项链表代码实现

  1. package main
  2. import "fmt"
  3. //定义链表节点结构
  4. type singleNode struct {
  5. no int
  6. name string
  7. next *singleNode //指向下一个数据地址,没有则为nil
  8. }
  9. //定义链表结构
  10. type singLinked struct {
  11. head *singleNode // 链表头数据
  12. tail *singleNode // 链表尾数据
  13. size int //长度
  14. }
  15. // InsertTail 从尾部插入数据
  16. func (singLinked *singLinked) InsertTail(linked *singleNode) {
  17. //数据校验
  18. if linked == nil {
  19. fmt.Println("数据为空")
  20. return
  21. }
  22. //若链表长度为0
  23. if singLinked.size == 0 {
  24. //直接将数据插入头
  25. singLinked.head = linked
  26. singLinked.tail = linked
  27. //最后一个节点next为nil
  28. linked.next = nil
  29. } else {
  30. //如果存在有数据,将数据插入到上一个数据的尾部
  31. singLinked.tail.next = linked
  32. //并将插入的数据next置为nil
  33. linked.next = nil
  34. //尾部数据录入
  35. singLinked.tail = linked
  36. }
  37. singLinked.size++
  38. }
  39. // InterHead 从头部插入数据
  40. func (singLinked *singLinked) InterHead(linked *singleNode) {
  41. //数据校验
  42. if linked == nil {
  43. fmt.Println("数据为空")
  44. return
  45. }
  46. //判断链表内是否存在数据
  47. if singLinked.size == 0 {
  48. singLinked.head = linked
  49. singLinked.tail = linked
  50. linked.next = nil
  51. } else {
  52. //如果有数据
  53. head := singLinked.head
  54. linked.next = head
  55. singLinked.head = linked
  56. }
  57. singLinked.size++
  58. }
  59. // InterNumber 根据编号大小进行插入排序
  60. func (singLinked *singLinked) InterNumber(linked *singleNode) {
  61. //数据校验
  62. if linked == nil {
  63. fmt.Println("数据为空")
  64. return
  65. }
  66. //创建辅助数据
  67. temp := singLinked.head
  68. for {
  69. if singLinked.size == 0 {
  70. //链表为空直接插入
  71. singLinked.head = linked
  72. singLinked.tail = linked
  73. linked.next = nil
  74. singLinked.size++
  75. break
  76. } else if temp.next == nil {
  77. //标识链表已到最后
  78. //表示最后一位了
  79. temp.next = linked
  80. singLinked.tail = linked
  81. singLinked.size++
  82. break
  83. } else if temp.no == linked.no {
  84. //当前节点的下一个节点编号大于需要插入的数据编号
  85. fmt.Println("编号冲突")
  86. break
  87. } else if linked.no < singLinked.head.no {
  88. //判断是否需要插入头节点
  89. linked.next = singLinked.head
  90. singLinked.head = linked
  91. singLinked.size++
  92. break
  93. } else if temp.next.no > linked.no {
  94. //判断当前的数据编号是否比插入数据的编号大
  95. linked.next = temp.next
  96. temp.next = linked
  97. singLinked.size++
  98. break
  99. }
  100. //循环
  101. temp = temp.next
  102. }
  103. }
  104. // GetHead 获取头节点
  105. func (singLinked *singLinked) GetHead() *singleNode {
  106. return singLinked.head
  107. }
  108. // Print 遍历读取数据操作
  109. func (singLinked *singLinked) Print() {
  110. fmt.Printf("当前链表内有%v个数据\n", singLinked.size)
  111. if singLinked.size == 0 {
  112. fmt.Println("链表为空")
  113. return
  114. }
  115. head := singLinked.GetHead()
  116. for head != nil {
  117. fmt.Printf("编号:%v 名称:%v\n", head.no, head.name)
  118. head = head.next
  119. }
  120. }
  121. // DeleteNo 根据编号删除节点
  122. func (singLinked *singLinked) DeleteNo(id int) {
  123. //数据校验
  124. if singLinked.size == 0 {
  125. fmt.Println("数据异常")
  126. return
  127. }
  128. //创建辅助节点
  129. temp := singLinked.head
  130. for {
  131. if singLinked.head.no == id {
  132. //是头节点,删除头结点
  133. singLinked.head = singLinked.head.next
  134. singLinked.size--
  135. break
  136. } else if temp.next.no == id {
  137. //不是头节点,删除节点数据
  138. temp.next = temp.next.next
  139. singLinked.size--
  140. break
  141. } else if temp.next == nil {
  142. //已经到最后了,并未找到该编号节点
  143. fmt.Println("未找到数据")
  144. break
  145. }
  146. temp = temp.next
  147. }
  148. }
  149. // Inquire 根据编号查询节点
  150. func (singLinked *singLinked) Inquire(id int) (singleNode *singleNode) {
  151. //数据校验
  152. if singLinked.size == 0 {
  153. fmt.Println("数据异常")
  154. return
  155. }
  156. //创建辅助节点
  157. temp := singLinked.head
  158. for {
  159. if singLinked.head.no == id {
  160. //是头节点返回头结点
  161. fmt.Println()
  162. singleNode = singLinked.head
  163. break
  164. } else if temp.next.no == id {
  165. //不是头节点,删除节点数据
  166. singleNode = temp.next
  167. break
  168. } else if temp.next == nil {
  169. //已经到最后了,并未找到该编号节点
  170. fmt.Println("未找到数据")
  171. break
  172. }
  173. temp = temp.next
  174. }
  175. return
  176. }
  177. // Modification 修改指定id节点
  178. func (singLinked *singLinked) Modification(id int, singleNode *singleNode) {
  179. //数据校验
  180. if singLinked.size == 0 || singleNode == nil {
  181. fmt.Println("数据异常")
  182. return
  183. }
  184. //创建辅助节点
  185. temp := singLinked.head
  186. for {
  187. if temp.next == nil {
  188. //已经到最后了,并未找到该编号节点
  189. fmt.Println("未找到数据")
  190. break
  191. } else if singLinked.head.no == id {
  192. //是头结点
  193. singLinked.head.name = singleNode.name
  194. break
  195. } else if temp.next.no == id {
  196. temp.next.name = singleNode.name
  197. break
  198. }
  199. temp = temp.next
  200. }
  201. return
  202. }
  203. // Destroy 链表销毁
  204. func (singLinked *singLinked) Destroy() {
  205. singLinked.head = nil
  206. singLinked.tail = nil
  207. singLinked.size = 0
  208. }
  209. //主函数
  210. func main() {
  211. //初始化
  212. linked := singLinked{}
  213. fmt.Println("==============尾插数据分割线===============")
  214. //添加操作
  215. linked.InsertTail(&singleNode{
  216. no: 7,
  217. name: "娃哈哈",
  218. })
  219. linked.InsertTail(&singleNode{
  220. no: 8,
  221. name: "营养快线",
  222. next: nil,
  223. })
  224. linked.Print()
  225. fmt.Println("==============编号插入排序分割线===============")
  226. linked.InterNumber(&singleNode{
  227. no: 1,
  228. name: "康师傅",
  229. next: nil,
  230. })
  231. linked.InterNumber(&singleNode{
  232. no: 9,
  233. name: "康师傅",
  234. next: nil,
  235. })
  236. linked.Print()
  237. fmt.Println("==============根据编号查询分割线===============")
  238. inquire := linked.Inquire(8)
  239. fmt.Println(inquire)
  240. fmt.Println("==============修改数据分割线===============")
  241. linked.Modification(8, &singleNode{name: "嘻嘻哈哈"})
  242. linked.Print()
  243. fmt.Println("==============根据id删除数据分割线===============")
  244. linked.DeleteNo(9)
  245. linked.DeleteNo(1)
  246. linked.DeleteNo(8)
  247. linked.DeleteNo(7)
  248. linked.Print()
  249. }

双向链表

双向链表也叫双链表,是链表的一种,它的每个数据结点中都有两个指针,分别指向直接后继和直接前驱。所以,从双向链表中的任意一个结点开始,都可以很方便地访问它的前驱结点和后继结点。一般我们都构造双向循环链表。

双向链表代码实现

  1. package main
  2. import "fmt"
  3. // Node 节点结构
  4. type Node struct {
  5. no int
  6. name string
  7. front *Node //前节点
  8. queen *Node //后节点
  9. }
  10. // Linked 链表结构
  11. type Linked struct {
  12. size int //节点个数
  13. head *Node //头节点
  14. trail *Node //尾结点
  15. }
  16. // InsertTrail 尾插法
  17. func (linked *Linked) InsertTrail(node *Node) {
  18. //数据校验
  19. if node == nil {
  20. fmt.Println("数据异常")
  21. return
  22. }
  23. temp := linked.head
  24. for true {
  25. if temp.queen == nil {
  26. break
  27. }
  28. temp = temp.queen
  29. }
  30. //找到最后一个节点将新节点插入最后
  31. temp.queen = node
  32. node.front = temp
  33. linked.size++
  34. }
  35. // InsertNumber 按照编号大小顺序插入
  36. func (linked *Linked) InsertNumber(node *Node) {
  37. //数据校验
  38. if node == nil {
  39. fmt.Println("数据异常")
  40. return
  41. }
  42. temp := linked.head
  43. for true {
  44. if temp.queen == nil {
  45. //遍历到最后,数据应插入最后
  46. temp.queen = node
  47. linked.trail = node
  48. linked.size++
  49. break
  50. } else if temp.no == node.no {
  51. fmt.Printf("数据ID冲突")
  52. break
  53. } else if temp.queen.no > node.no {
  54. //下一个节点的id比插入节点id大
  55. //插入逻辑,先将插入节点上指针和下指针分别指向上下节点再进行上下节点关联新增节点步骤
  56. node.queen = temp.queen
  57. node.front = temp
  58. //判断是否还有下个节点
  59. if temp.queen != nil {
  60. temp.queen.front = node
  61. }
  62. temp.queen = node
  63. linked.size++
  64. break
  65. }
  66. temp = temp.queen
  67. }
  68. }
  69. // DeleteNode 删除节点根据id删除
  70. func (linked *Linked) DeleteNode(id int) {
  71. temp := linked.head
  72. for true {
  73. if temp.queen == nil {
  74. fmt.Println("未找到该id节点")
  75. break
  76. } else if temp.queen.no == id {
  77. temp.queen = temp.queen.queen
  78. //判断是否还有下个节点
  79. if temp.queen != nil {
  80. temp.queen.front = temp
  81. }
  82. linked.size--
  83. break
  84. }
  85. temp = temp.queen
  86. }
  87. }
  88. // InquireLinked 查询链表所有数据
  89. func (linked *Linked) InquireLinked() {
  90. fmt.Printf("当前链表内有%v个数据\n", linked.size)
  91. if linked.size == 0 {
  92. fmt.Println("链表为空")
  93. }
  94. temp := linked.head
  95. for temp.queen != nil {
  96. fmt.Printf("编号:%v 名称:%v\n", temp.queen.no, temp.queen.name)
  97. temp = temp.queen
  98. }
  99. }
  100. // InversePrinting 倒序打印
  101. func (linked *Linked) InversePrinting() {
  102. if linked.size == 0 {
  103. fmt.Println("链表为空")
  104. }
  105. temp := linked.trail
  106. for true {
  107. fmt.Printf("编号:%v 名称:%v\n", temp.no, temp.name)
  108. temp = temp.front
  109. if temp.front == nil {
  110. break
  111. }
  112. }
  113. }
  114. func main() {
  115. //初始化
  116. linked := Linked{
  117. head: &Node{
  118. no: 0,
  119. name: "头结点",
  120. },
  121. trail: nil,
  122. }
  123. linked.InsertTrail(&Node{
  124. no: 2,
  125. name: "王老吉",
  126. })
  127. linked.InsertNumber(
  128. &Node{
  129. no: 4,
  130. name: "加多宝",
  131. front: nil,
  132. queen: nil,
  133. })
  134. linked.InsertNumber(
  135. &Node{
  136. no: 3,
  137. name: "可口可乐",
  138. front: nil,
  139. queen: nil,
  140. })
  141. linked.InsertNumber(
  142. &Node{
  143. no: 1,
  144. name: "雪碧",
  145. front: nil,
  146. queen: nil,
  147. })
  148. linked.DeleteNode(5)
  149. linked.InquireLinked()
  150. fmt.Println("=========分割线===========")
  151. linked.InversePrinting()
  152. }

环形链表

循环链表是一种特殊的单链表。循环跟单链表唯一的区别就在尾结点。单链表的尾结点指针指向空地址,表示这就是最后的结点了。而循环链表的尾结点指针是指向链表的头结点。

环形链表也可以解决约瑟夫问题

环形链表代码实现

  1. package main
  2. import "fmt"
  3. //定义节点结构体
  4. type Node struct {
  5. no int
  6. name string
  7. next *Node //下一个节点
  8. }
  9. // AnnularLinked 环形链表结构
  10. type AnnularLinked struct {
  11. size int
  12. head *Node //头结点
  13. tail *Node // 尾结点
  14. }
  15. // Insertion 添加数据
  16. func (linked *AnnularLinked) Insertion(node *Node) {
  17. //判断链表是否有数据
  18. if linked.size == 0 {
  19. linked.head = node
  20. linked.head.next = node
  21. linked.tail = node
  22. linked.size++
  23. return
  24. }
  25. //如果有数据
  26. linked.tail.next = node
  27. node.next = linked.head
  28. linked.tail = node
  29. linked.size++
  30. }
  31. // Print 打印循环链表
  32. func (linked *AnnularLinked) Print() {
  33. fmt.Printf("当前链表内有%v个数据\n", linked.size)
  34. if linked.size == 0 {
  35. fmt.Println("链表为空")
  36. return
  37. }
  38. temp := linked.head
  39. for {
  40. fmt.Printf("编号:%v 名称:%v\n", temp.no, temp.name)
  41. if temp.next == linked.head {
  42. break
  43. }
  44. temp = temp.next
  45. }
  46. }
  47. // DeleteNode 删除某个节点
  48. func (linked *AnnularLinked) DeleteNode(id int) {
  49. //判断链表是否为空
  50. if linked.size == 0 {
  51. fmt.Println("空链表,无法执行删除")
  52. return
  53. }
  54. //判断如果是删除头节点
  55. if linked.head.no == id {
  56. linked.tail.next = linked.head.next
  57. linked.head = linked.head.next
  58. linked.size--
  59. return
  60. }
  61. temp := linked.head
  62. for true {
  63. if temp.next.no == id {
  64. //如果删除的是尾节点
  65. if temp.next == linked.tail {
  66. linked.tail = temp
  67. }
  68. temp.next = temp.next.next
  69. linked.size--
  70. break
  71. } else if temp.next == linked.head {
  72. fmt.Println("未找到该id节点")
  73. break
  74. }
  75. temp = temp.next
  76. }
  77. }
  78. func main() {
  79. //初始化
  80. linked := AnnularLinked{
  81. size: 0,
  82. head: &Node{},
  83. tail: nil,
  84. }
  85. linked.Insertion(&Node{
  86. no: 1,
  87. name: "嘻嘻嘻",
  88. next: nil,
  89. })
  90. linked.Insertion(&Node{
  91. no: 2,
  92. name: "嘻嘻嘻",
  93. next: nil,
  94. })
  95. linked.Insertion(&Node{
  96. no: 3,
  97. name: "嘻嘻嘻",
  98. next: nil,
  99. })
  100. linked.Insertion(&Node{
  101. no: 4,
  102. name: "嘻嘻嘻",
  103. next: nil,
  104. })
  105. linked.DeleteNode(2)
  106. linked.DeleteNode(1)
  107. linked.DeleteNode(3)
  108. linked.DeleteNode(4)
  109. linked.Print()
  110. }

本文借鉴:通俗易懂讲解 链表 - 知乎 (zhihu.com)

尚硅谷Java数据结构与java算法(Java数据结构与算法)_哔哩哔哩_bilibili

GO语言数据结构之链表的更多相关文章

  1. C语言数据结构-单链表的实现-初始化、销毁、长度、查找、前驱、后继、插入、删除、显示操作

    1.数据结构-单链表的实现-C语言 typedef struct LNode { int data; struct LNode* next; } LNode,*LinkList; //这两者等价.Li ...

  2. C语言数据结构-创建链表的四种方法

    结点类型: typedef int datatype; typedef struct NODE{ datatype data; struct NODE *next; }Node,*LinkList; ...

  3. C语言实现单链表-03版

    在C语言实现单链表-02版中我们只是简单的更新一下链表的组织方式: 它没有更多的更新功能,因此我们这个版本将要完成如下功能: Problem 1,搜索相关节点: 2,前插节点: 3,后追加节点: 4, ...

  4. linux内核数据结构之链表

    linux内核数据结构之链表 1.前言 最近写代码需用到链表结构,正好公共库有关于链表的.第一眼看时,觉得有点新鲜,和我之前见到的链表结构不一样,只有前驱和后继指针,而没有数据域.后来看代码注释发现该 ...

  5. Java描述数据结构之链表的增删改查

    链表是一种常见的基础数据结构,它是一种线性表,但在内存中它并不是顺序存储的,它是以链式进行存储的,每一个节点里存放的是下一个节点的"指针".在Java中的数据分为引用数据类型和基础 ...

  6. C/C++语言实现单链表(带头结点)

    彻底理解链表中为何使用二级指针或者一级指针的引用 数据结构之链表-链表实现及常用操作(C++篇) C语言实现单链表,主要功能为空链表创建,链表初始化(头插法),链表元素读取,按位置插入,(有序链表)按 ...

  7. 深入理解Redis 数据结构—双链表

    在 Redis 数据类型中的列表list,对数据的添加和删除常用的命令有 lpush,rpush,lpop,rpop,其中 l 表示在左侧,r 表示在右侧,可以在左右两侧做添加和删除操作,说明这是一个 ...

  8. 学习javascript数据结构(二)——链表

    前言 人生总是直向前行走,从不留下什么. 原文地址:学习javascript数据结构(二)--链表 博主博客地址:Damonare的个人博客 正文 链表简介 上一篇博客-学习javascript数据结 ...

  9. C语言实现单链表-02版

    我们在C语言实现单链表-01版中实现的链表非常简单: 但是它对于理解单链表是非常有帮助的,至少我就是这样认为的: 简单的不能再简单的东西没那么实用,所以我们接下来要大规模的修改啦: Problem 1 ...

随机推荐

  1. 用Fiddler抓不到https的包?因为你姿势不对!往这看!

    前言 刚入行测试的小伙伴可能不知道,Fiddler默认抓http的包,如果要抓https的包,是需要装证书的!什么鬼证书?不明白的话继续往下看. Fiddler 抓取 https 数据 第一步:下载 ...

  2. java 从零开始手写 RPC (01) 基于 websocket 实现

    RPC 解决的问题 RPC 主要是为了解决的两个问题: 解决分布式系统中,服务之间的调用问题. 远程调用时,要能够像本地调用一样方便,让调用者感知不到远程调用的逻辑. 这一节我们来学习下如何基于 we ...

  3. Xcode相关

    Xcode相关的路径 Provisioning Profiles存放路径:~/Library/MobileDevice/Provisioning Profiles 所有模拟器(包括历史模拟器):~/L ...

  4. uoj21 缩进优化(整除分块,乱搞)

    题目大意: 给定一个长度为\(n\)的序列 让你找一个\(x\),使得\(ans\)尽可能小 其中$$ans=\sum_{i=1}^{n}\lfloor\frac{a_i}{x}\rfloor + \ ...

  5. Mybatis一级缓存的锅

    问题背景 项目开发中有一个树形数据结构,不像经典组织结构树.菜单级别树,我们这个树形结构是用户后期手动建立起来的关系.因此数据库表结构为两张表:数据记录表.记录关系表,通过业务规则限制,形成的树形结构 ...

  6. VS Code Just My Code Debugging

    VS Code Just My Code Debugging VS Code for C++ doesn't support Just My Code Refer here: Add support ...

  7. python画图的工具及网站

    ①Gallery - Matplotlib 3.4.3 documentation 学会模仿并超越 ②Examples - Apache ECharts js网页端动态展示 ③WEB色見本 原色大辞典 ...

  8. Sharding-JDBC基本使用,整合Springboot实现分库分表,读写分离

    结合上一篇docker部署的mysql主从, 本篇主要讲解SpringBoot项目结合Sharding-JDBC如何实现分库分表.读写分离. 一.Sharding-JDBC介绍 1.这里引用官网上的介 ...

  9. windows下安装dirmap详细教程

    今天安装一下dirmap,纯小白非常详细的安装过程 1.先去下载dirmap 下载地址:https://github.com/H4ckForJob/dirmap 点这个绿色的code,然后再点下面这个 ...

  10. Spring Boot 2.5.0 重新设计的spring.sql.init 配置有何用?

    前几天Spring Boot 2.5.0发布了,其中提到了关于Datasource初始化机制的调整,有读者私信想了解这方面做了什么调整.那么今天就要详细说说这个重新设计的配置内容,并结合实际情况说说我 ...