Go实现双向链表的示例代码
来到golang学习网的大家,相信都是编程学习爱好者,希望在这里学习Golang相关编程知识。下面本篇文章就来带大家聊聊《Go实现双向链表的示例代码》,介绍一下双向链表,希望对大家的知识积累有所帮助,助力实战开发!
本文介绍什么是链表,常见的链表有哪些,然后介绍链表这种数据结构会在哪些地方可以用到,以及 Redis 队列是底层的实现,通过一个小实例来演示 Redis 队列有哪些功能,最后通过 Go 实现一个双向链表。
目录
1、链表
- 1.1 说明
- 1.2 单向链表
- 1.3 循环链表
- 1.4 双向链表
2、redis队列
- 2.1 说明
- 2.2 应用场景
- 2.3 演示
3、Go双向链表
- 3.1 说明
- 3.2 实现
4、总结
5、参考文献
- 1、链表
- 1.1 说明
链表(Linked list)是一种常见的基础数据结构,是一种线性表,但是并不会按线性的顺序存储数据,而是在每一个节点里存到下一个节点的指针(Pointer)。由于不必须按顺序存储,链表在插入的时候可以达到O(1)的复杂度,比另一种线性表顺序表快得多,但是查找一个节点或者访问特定编号的节点则需要O(n)的时间,而顺序表相应的时间复杂度分别是O(logn)和O(1)。
链表有很多种不同的类型:单向链表,双向链表以及循环链表。
优势:
可以克服数组链表需要预先知道数据大小的缺点,链表结构可以充分利用计算机内存空间,实现灵活的内存动态管理。链表允许插入和移除表上任意位置上的节点。
劣势:
由于链表增加了节点指针,空间开销比较大。链表一般查找数据的时候需要从第一个节点开始每次访问下一个节点,直到访问到需要的位置,查找数据比较慢。
用途:
常用于组织检索较少,而删除、添加、遍历较多的数据。
如:文件系统、LRU cache、Redis 列表、内存管理等。
1.2 单向链表
链表中最简单的一种是单向链表,
一个单向链表的节点被分成两个部分。它包含两个域,一个信息域和一个指针域。第一个部分保存或者显示关于节点的信息,第二个部分存储下一个节点的地址,而最后一个节点则指向一个空值。单向链表只可向一个方向遍历。
单链表有一个头节点head,指向链表在内存的首地址。链表中的每一个节点的数据类型为结构体类型,节点有两个成员:整型成员(实际需要保存的数据)和指向下一个结构体类型节点的指针即下一个节点的地址(事实上,此单链表是用于存放整型数据的动态数组)。链表按此结构对各节点的访问需从链表的头找起,后续节点的地址由当前节点给出。无论在表中访问哪个节点,都需要从链表的头开始,顺序向后查找。链表的尾节点由于无后续节点,其指针域为空,写作为NULL。
1.3 循环链表
循环链表是与单向链表一样,是一种链式的存储结构,所不同的是,循环链表的最后一个结点的指针是指向该循环链表的第一个结点或者表头结点,从而构成一个环形的链。
循环链表的运算与单链表的运算基本一致。所不同的有以下几点:
1、在建立一个循环链表时,必须使其最后一个结点的指针指向表头结点,而不是像单链表那样置为NULL。
2、在判断是否到表尾时,是判断该结点链域的值是否是表头结点,当链域的值等于表头指针时,说明已到表尾。而非象单链表那样判断链域的值是否为NULL。
1.4 双向链表
双向链表其实是单链表的改进,当我们对单链表进行操作时,有时你要对某个结点的直接前驱进行操作时,又必须从表头开始查找。这是由单链表结点的结构所限制的。因为单链表每个结点只有一个存储直接后继结点地址的链域,那么能不能定义一个既有存储直接后继结点地址的链域,又有存储直接前驱结点地址的链域的这样一个双链域结点结构呢?这就是双向链表。
在双向链表中,结点除含有数据域外,还有两个链域,一个存储直接后继结点地址,一般称之为右链域(当此“连接”为最后一个“连接”时,指向空值或者空列表);一个存储直接前驱结点地址,一般称之为左链域(当此“连接”为第一个“连接”时,指向空值或者空列表)。
2、redis队列
2.1 说明
Redis 列表是简单的字符串列表,按照插入顺序排序。你可以添加一个元素到列表的头部(左边)或者尾部(右边)
Redis 列表使用两种数据结构作为底层实现:双端列表(linkedlist)、压缩列表(ziplist)
通过配置文件中(list-max-ziplist-entries、list-max-ziplist-value)来选择是哪种实现方式
在数据量比较少的时候,使用双端链表和压缩列表性能差异不大,但是使用压缩列表更能节约内存空间
redis 链表的实现源码 redis src/adlist.h
2.2 应用场景
消息队列,秒杀项目
秒杀项目:
提前将需要的商品码信息存入 Redis 队列,在抢购的时候每个用户都从 Redis 队列中取商品码,由于 Redis 是单线程的,同时只能有一个商品码被取出,取到商品码的用户为购买成功,而且 Redis 性能比较高,能抗住较大的用户压力。
2.3 演示
如何通过 Redis 队列中防止并发情况下商品超卖的情况。
假设:
网站有三件商品需要卖,我们将数据存入 Redis 队列中
1、 将三个商品码(10001、10002、10003)存入 Redis 队列中
# 存入商品 RPUSH commodity:queue 10001 10002 10003
2、 存入以后,查询数据是否符合预期
# 查看全部元素 LRANGE commodity:queue 0 -1 # 查看队列的长度 LLEN commodity:queue
3、 抢购开始,获取商品码,抢到商品码的用户则可以购买(由于 Redis 是单线程的,同一个商品码只能被取一次
# 出队 LPOP commodity:queue
这里了解到 Redis 列表是怎么使用的,下面就用 Go 语言实现一个双向链表来实现这些功能。
3、Go双向链表
3.1 说明
这里只是用 Go 语言实现一个双向链表,实现:查询链表的长度、链表右端插入数据、左端取数据、取指定区间的节点等功能( 类似于 Redis 列表的中的 RPUSH、LRANGE、LPOP、LLEN功能 )。
3.2 实现
节点定义
双向链表有两个指针,分别指向前一个节点和后一个节点
链表表头 prev 的指针为空,链表表尾 next 的指针为空
// 链表的一个节点 type ListNode struct { prev *ListNode // 前一个节点 next *ListNode // 后一个节点 value string // 数据 } // 创建一个节点 func NewListNode(value string) (listNode *ListNode) { listNode = &ListNode{ value: value, } return } // 当前节点的前一个节点 func (n *ListNode) Prev() (prev *ListNode) { prev = n.prev return } // 当前节点的前一个节点 func (n *ListNode) Next() (next *ListNode) { next = n.next return } // 获取节点的值 func (n *ListNode) GetValue() (value string) { if n == nil { return } value = n.value return }
定义一个链表
链表为了方便操作,定义一个结构体,可以直接从表头、表尾进行访问,定义了一个属性 len ,直接可以返回链表的长度,直接查询链表的长度就不用遍历时间复杂度从 O(n) 到 O(1)。
// 链表 type List struct { head *ListNode // 表头节点 tail *ListNode // 表尾节点 len int // 链表的长度 } // 创建一个空链表 func NewList() (list *List) { list = &List{ } return } // 返回链表头节点 func (l *List) Head() (head *ListNode) { head = l.head return } // 返回链表尾节点 func (l *List) Tail() (tail *ListNode) { tail = l.tail return } // 返回链表长度 func (l *List) Len() (len int) { len = l.len return }
在链表的右边插入一个元素
// 在链表的右边插入一个元素 func (l *List) RPush(value string) { node := NewListNode(value) // 链表未空的时候 if l.Len() == 0 { l.head = node l.tail = node } else { tail := l.tail tail.next = node node.prev = tail l.tail = node } l.len = l.len + 1 return }
从链表左边取出一个节点
// 从链表左边取出一个节点 func (l *List) LPop() (node *ListNode) { // 数据为空 if l.len == 0 { return } node = l.head if node.next == nil { // 链表未空 l.head = nil l.tail = nil } else { l.head = node.next } l.len = l.len - 1 return }
通过索引查找节点
通过索引查找节点,如果索引是负数则从表尾开始查找。
自然数和负数索引分别通过两种方式查找节点,找到指定索引或者是链表全部查找完则查找完成。
// 通过索引查找节点 // 查不到节点则返回空 func (l *List) Index(index int) (node *ListNode) { // 索引为负数则表尾开始查找 if index 0 && node != nil; index-- { node = node.next } } return }
返回指定区间的元素
// 返回指定区间的元素 func (l *List) Range(start, stop int) (nodes []*ListNode) { nodes = make([]*ListNode, 0) // 转为自然数 if start4、总结
到这里关于链表的使用已经结束,介绍链表是有哪些(单向链表,双向链表以及循环链表),也介绍了链表的应用场景(Redis 列表使用的是链表作为底层实现),最后用 Go 实现了双向链表,演示了链表在 Go 语言中是怎么使用的,大家可以在项目中更具实际的情况去使用。
5、参考文献
项目地址:go 实现队列
https://github.com/link1st/link1st/tree/master/linked
今天关于《Go实现双向链表的示例代码》的内容就介绍到这里了,是不是学起来一目了然!想要了解更多关于golang的内容请关注golang学习网公众号!

- 上一篇
- golang实现对docker容器心跳监控功能

- 下一篇
- Go 防止 goroutine 泄露的方法
-
- 怕孤独的乌龟
- 好细啊,收藏了,感谢作者大大的这篇文章内容,我会继续支持!
- 2023-03-23 14:42:39
-
- 聪明的朋友
- 很有用,一直没懂这个问题,但其实工作中常常有遇到...不过今天到这,看完之后很有帮助,总算是懂了,感谢博主分享技术贴!
- 2023-02-17 21:53:03
-
- 眯眯眼的小蘑菇
- 这篇文章内容出现的刚刚好,细节满满,很有用,已收藏,关注作者大大了!希望作者大大能多写Golang相关的文章。
- 2023-01-31 09:38:32
-
- 明理的故事
- 这篇博文出现的刚刚好,太细致了,真优秀,已收藏,关注师傅了!希望师傅能多写Golang相关的文章。
- 2023-01-26 15:25:38
-
- Golang · Go教程 | 28分钟前 |
- DebianSyslog在虚拟机中的实用攻略
- 467浏览 收藏
-
- Golang · Go教程 | 8小时前 |
- DebianOpenSSL安装失败的终极解决方案
- 501浏览 收藏
-
- Golang · Go教程 | 9小时前 |
- Debian数据快速提取技巧
- 216浏览 收藏
-
- Golang · Go教程 | 12小时前 |
- Debian系统JS依赖管理终极攻略
- 218浏览 收藏
-
- Golang · Go教程 | 14小时前 |
- Debian上Hadoop作业调度实用技巧
- 100浏览 收藏
-
- Golang · Go教程 | 14小时前 |
- Go语言闭包误区与匿名函数深度解析
- 222浏览 收藏
-
- Golang · Go教程 | 14小时前 |
- Debian系统安全回收数据的正确攻略
- 111浏览 收藏
-
- Golang · Go教程 | 16小时前 |
- Debian高效fetch技巧与使用攻略
- 125浏览 收藏
-
- 前端进阶之JavaScript设计模式
- 设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
- 542次学习
-
- GO语言核心编程课程
- 本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
- 508次学习
-
- 简单聊聊mysql8与网络通信
- 如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
- 497次学习
-
- JavaScript正则表达式基础与实战
- 在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
- 487次学习
-
- 从零制作响应式网站—Grid布局
- 本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
- 484次学习
-
- 笔灵AI生成答辩PPT
- 探索笔灵AI生成答辩PPT的强大功能,快速制作高质量答辩PPT。精准内容提取、多样模板匹配、数据可视化、配套自述稿生成,让您的学术和职场展示更加专业与高效。
- 15次使用
-
- 知网AIGC检测服务系统
- 知网AIGC检测服务系统,专注于检测学术文本中的疑似AI生成内容。依托知网海量高质量文献资源,结合先进的“知识增强AIGC检测技术”,系统能够从语言模式和语义逻辑两方面精准识别AI生成内容,适用于学术研究、教育和企业领域,确保文本的真实性和原创性。
- 24次使用
-
- AIGC检测-Aibiye
- AIbiye官网推出的AIGC检测服务,专注于检测ChatGPT、Gemini、Claude等AIGC工具生成的文本,帮助用户确保论文的原创性和学术规范。支持txt和doc(x)格式,检测范围为论文正文,提供高准确性和便捷的用户体验。
- 30次使用
-
- 易笔AI论文
- 易笔AI论文平台提供自动写作、格式校对、查重检测等功能,支持多种学术领域的论文生成。价格优惠,界面友好,操作简便,适用于学术研究者、学生及论文辅导机构。
- 42次使用
-
- 笔启AI论文写作平台
- 笔启AI论文写作平台提供多类型论文生成服务,支持多语言写作,满足学术研究者、学生和职场人士的需求。平台采用AI 4.0版本,确保论文质量和原创性,并提供查重保障和隐私保护。
- 35次使用
-
- Golangmap实践及实现原理解析
- 2022-12-28 505浏览
-
- 试了下Golang实现try catch的方法
- 2022-12-27 502浏览
-
- Go语言中Slice常见陷阱与避免方法详解
- 2023-02-25 501浏览
-
- Golang中for循环遍历避坑指南
- 2023-05-12 501浏览
-
- Go语言中的RPC框架原理与应用
- 2023-06-01 501浏览