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 start <p><span style="color: #ff0000"><strong>4、总结</strong></span></p> <p>到这里关于链表的使用已经结束,介绍链表是有哪些(单向链表,双向链表以及循环链表),也介绍了链表的应用场景(Redis 列表使用的是链表作为底层实现),最后用 Go 实现了双向链表,演示了链表在 Go 语言中是怎么使用的,大家可以在项目中更具实际的情况去使用。</p> <p><span style="color: #ff0000"><strong>5、参考文献</strong></span></p> <p><a target='_blank' href='https://www.17golang.com/gourl/?redirect=MDAwMDAwMDAwML57hpSHp6VpkrqbYLx2eayza4KafaOkbLS3zqSBrJvPsa5_0Ia6sWuR4Juaq6t9nq5roGCUgXuytMyerpmfntrIZp3WmbumpZKtnJiun2mtv7JxY5GQraix3Lx-g4WMmrGth6eKtrp_h6mGcq-Fhna0fIWaebKGor-qu2iDdniVvoiclJKnrbGGmHqar5ybaLSNoJx9s52ist20pIKJhJe9rpyh' rel='nofollow'>维基百科 链表</a></p> <p><a target='_blank' href='https://www.17golang.com/gourl/?redirect=MDAwMDAwMDAwML57hpSHp6VpkrqbYLx2eayza4KafaOkbLS3zqSBrJvPsa5_0Ia6sWuR4Juaq6t9nq5roGCUgXuytMyero5ko5XFfIfNhNCyr5q5aZjEoIKkyKaOZnxsg6S_qtKyfauEz62tf8-St7VthaqCnLGGgp-yo31jiaaGsbS3zW2DeYzfsmZ-3oWVuWqR4IqasYNtcQ' rel='nofollow'>github redis</a></p> <p>项目地址:<a target='_blank' href='https://www.17golang.com/gourl/?redirect=MDAwMDAwMDAwML57hpSHp6VpkrqbYLx2eayza4KafaOkbLS3zqSBrJvPsa5_0Ia6sWuR4Juaq6t9nq5roGCUgXuytMyero5ko5XFfIfNhNCyr5q5aaPDiWWmspGGYHxrsajH0Nmwl2WI28h8e9CStp2tkb5-YLyKearHgKSlkWuPo67cs6J9q4XQvoiCmIWntqWHuoKbr5x1Z76mhal_jaBttKq7soJkhN-xZoaVkd2-o4e3bW0' rel='nofollow'>go 实现队列</a></p> <p><a target='_blank' href='https://www.17golang.com/gourl/?redirect=MDAwMDAwMDAwML57hpSHp6VpkrqbYLx2eayza4KafaOkbLS3zqSBrJvPsa5_0Ia6sWuR4Juaq6t9nq5roGCUgXuytMyero5ko5XFfIfNhNCyr5q5aaPDiWWmspGGYHxrsajH0Nmwl2WI28h8e9CStp2tkb5-YLyKearHgKSlkWuPo67cs6J9q4XQvoiCmIWntqWHuoKbr5x1Z76mhal_jaBttKq7soJkhN-xZoaVkd2-o4e3bW0' rel='nofollow'>https://github.com/link1st/link1st/tree/master/linked</a></p> <p>今天关于《Go实现双向链表的示例代码》的内容就介绍到这里了,是不是学起来一目了然!想要了解更多关于golang的内容请关注golang学习网公众号!</p>

- 上一篇
- 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教程 | 17小时前 |
- TigervncDebian多用户共享桌面超简单教程
- 482浏览 收藏
-
- Golang · Go教程 | 1天前 |
- Go语言新手必看!切片vs数组,一次搞定这两个核心知识点
- 472浏览 收藏
-
- Golang · Go教程 | 1天前 |
- Docker在Debian上运行超简单教程(保姆级教学)
- 210浏览 收藏
-
- Golang · Go教程 | 1天前 |
- Debian设置hostname踩坑记录:权限问题大揭秘
- 334浏览 收藏
-
- Golang · Go教程 | 1天前 |
- Debian装SQLServer?这些问题你一定要注意!
- 284浏览 收藏
-
- Golang · Go教程 | 2天前 |
- Debian系统下Jenkins自动化部署脚本教学
- 367浏览 收藏
-
- Golang · Go教程 | 2天前 |
- DebianSwap服务器应用实测,这些场景真的用得上!
- 319浏览 收藏
-
- Golang · Go教程 | 2天前 |
- Debian跑TigerVNC实测!真香警告,快来看看性能咋样~
- 171浏览 收藏
-
- Golang · Go教程 | 2天前 |
- 在Debian上玩转SQLServer备份还原,手把手教你一步步操作
- 498浏览 收藏
-
- Golang · Go教程 | 2天前 |
- DebianOverlay不会玩?手把手教你轻松定制化安装
- 258浏览 收藏
-
- Golang · Go教程 | 2天前 |
- Go语言实战:time.Ticker&time.After用法区别及避坑技巧
- 240浏览 收藏
-
- Golang · Go教程 | 2天前 |
- Debian系统如何快速定位&干掉那些讨厌的僵尸进程
- 317浏览 收藏
-
- 前端进阶之JavaScript设计模式
- 设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
- 542次学习
-
- GO语言核心编程课程
- 本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
- 508次学习
-
- 简单聊聊mysql8与网络通信
- 如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
- 497次学习
-
- JavaScript正则表达式基础与实战
- 在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
- 487次学习
-
- 从零制作响应式网站—Grid布局
- 本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
- 484次学习
-
- 茅茅虫AIGC检测
- 茅茅虫AIGC检测,湖南茅茅虫科技有限公司倾力打造,运用NLP技术精准识别AI生成文本,提供论文、专著等学术文本的AIGC检测服务。支持多种格式,生成可视化报告,保障您的学术诚信和内容质量。
- 18次使用
-
- 赛林匹克平台(Challympics)
- 探索赛林匹克平台Challympics,一个聚焦人工智能、算力算法、量子计算等前沿技术的赛事聚合平台。连接产学研用,助力科技创新与产业升级。
- 50次使用
-
- 笔格AIPPT
- SEO 笔格AIPPT是135编辑器推出的AI智能PPT制作平台,依托DeepSeek大模型,实现智能大纲生成、一键PPT生成、AI文字优化、图像生成等功能。免费试用,提升PPT制作效率,适用于商务演示、教育培训等多种场景。
- 57次使用
-
- 稿定PPT
- 告别PPT制作难题!稿定PPT提供海量模板、AI智能生成、在线协作,助您轻松制作专业演示文稿。职场办公、教育学习、企业服务全覆盖,降本增效,释放创意!
- 53次使用
-
- Suno苏诺中文版
- 探索Suno苏诺中文版,一款颠覆传统音乐创作的AI平台。无需专业技能,轻松创作个性化音乐。智能词曲生成、风格迁移、海量音效,释放您的音乐灵感!
- 57次使用
-
- 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浏览