【数据结构】链表 - Go 语言实现
文章目录
- 一、简介
- 二、最简单的链表
- 三、循环链表
- 1. 初始化循环链表
- 2. 创建一个指定大小 N 的循环链表,值全为空
- 3. 获取上一个或下一个节点
- 4. 获取第 n 个节点
- 5. 获取链表长度
- 四、数组和链表
- 五、总结
- 参考链接
一、简介
数据结构是用来组织数据的,如何将一个数据关联到另外一个数据呢?
链表可以将数据和数据之间关联起来,从一个数据指向另外一个数据。
定义:
链表由一个个数据节点(一个节点是一个结构体)组成,它是一个递归结构,要么它是空的,要么它存在一个指向另外一个数据节点的引用。
链表,可以说是最基础的数据结构。
二、最简单的链表
结构体 LinkNode (链表的一个节点)有两个字段,一个字段存放数据 Data,另一个字段是一个指针,指向下一个节点 NextNode 。这种从一个数据节点指向下一个数据节点的结构,都可以叫做链表。
package mainimport ("fmt"
)type LinkNode struct { //链表的一个数据节点的结构体Data int64NextNode *LinkNode
}func main() {// 新的节点node := new(LinkNode) //使用 new 实例化一个结构体,返回指向结构体的指针node.Data = 2// 新的节点node1 := new(LinkNode)node1.Data = 3node.NextNode = node1 // node1 链接到 node 节点上// 新的节点node2 := new(LinkNode)node2.Data = 4node1.NextNode = node2 // node2 链接到 node1 节点上// 按顺序打印数据nowNode := nodefor {if nowNode != nil {// 打印节点值fmt.Println(nowNode.Data)// 获取下一个节点nowNode = nowNode.NextNodecontinue}// 如果下一个节点为空,表示链表结束了break}
}
输出结果:
2
3
4
有些书籍,把链表做了很细的划分,比如单链表,双链表,循环单链表,循环双链表,其实没有必要强行分类,链表就是从一个数据指向另外一个数据,一种将数据和数据关联起来的结构而已。
好吧,我们还是要知道是什么:
- 单链表,就是链表是单向的,像我们上面这个结构一样,可以一直往下找到下一个数据节点,它只有一个方向,它不能往回找。
- 双链表,每个节点既可以找到它之前的节点,也可以找到之后的节点,是双向的。(有两个指针字段,一个指向上一个节点,一个指向下一个节点)
- 循环链表,就是它一直往下找数据节点,最后回到了自己那个节点,首尾相接,形成了一个回路。循环单链表和循环双链表的区别就是,一个只能一个方向走,一个两个方向都可以走。
三、循环链表
我们来实现一个循环链表 Ring(集链表大成者),参考 Golang 标准库 container/ring::
// 循环链表
type Ring struct {next, prev *Ring // 前驱和后驱节点Value interface{} // 数据
}
该循环链表有一个三个字段,next 表示后驱节点,prev 表示前驱节点,Value 表示值。
接下来是该结构上的各操作:
1. 初始化循环链表
初始化一个空的循环链表:
package mainimport ("fmt"
)// 循环链表
type Ring struct {next, prev *Ring // 前驱和后驱节点Value interface{} // 数据
}// 初始化空的循环链表,前驱和后驱都指向自己,因为是循环的
func (r *Ring) init() *Ring {r.next = rr.prev = rreturn r
}
func main() {r := new(Ring)fmt.Println(r.init())
}
输出结果:
&{0xc0000a43a0 0xc0000a43a0 <nil>}
因为绑定前驱和后驱节点为自己,没有循环,时间复杂度为:O(1)。
2. 创建一个指定大小 N 的循环链表,值全为空
package mainimport ("fmt"
)// 循环链表
type Ring struct {next, prev *Ring // 前驱和后驱节点Value interface{} // 数据,空接口类型的变量可以存储任意类型的变量
}func New(n int) *Ring {if n <= 0 {return nil}r := new(Ring) //新节点p := rfor i := 1; i < n; i++ { //创建n-1个新节点p.next = &Ring{prev: p} //新节点p = p.next //p 指向新节点}p.next = r //首尾相接r.prev = preturn r
}func main() {fmt.Println(New(3))
}
会连续绑定前驱和后驱节点,时间复杂度为:O(n)。
3. 获取上一个或下一个节点
// 获取下一个节点
func (r *Ring) Next() *Ring {if r.next == nil {return r.init()}return r.next
}
// 获取上一个节点
func (r *Ring) Prev() *Ring {if r.next == nil {return r.init()}return r.prev
}
获取前驱或后驱节点,时间复杂度为:O(1)。
4. 获取第 n 个节点
因为链表是循环的,当 n 为负数,表示从表头往前遍历,否则往后面遍历:
func (r *Ring) Move(n int) *Ring {if r.next == nil {return r.init()}switch {case n < 0:for ; n < 0; n++ {r = r.prev}case n > 0:for ; n > 0; n-- {r = r.next}}return r
}
因为需要遍历 n 次,所以时间复杂度为:O(n)。
5. 获取链表长度
// 查看循环链表长度
func (r *Ring) Len() int {n := 0if r != nil {n = 1for p := r.Next(); p != r; p = p.next {n++}}return n
}
通过循环,当引用回到自己,那么计数完毕,时间复杂度:O(n)。
因为循环链表还不够强壮,不知道起始节点是哪个,计数链表长度还要遍历,所以用循环链表实现的双端队列就出现了,一般具体编程都使用更高层次的数据结构。
四、数组和链表
数组是编程语言作为一种 基本类型 提供出来的,相同数据类型的元素按一定顺序排列的集合。
它的作用只有一种:存放数据,让你很快能找到存的数据。如果你不去额外改进它,它就只是存放数据而已,它不会将一个数据节点和另外一个数据节点关联起来。比如建立一个大小为 5 的数组 array:
package mainimport "fmt"func main() {array := [5]int64{}fmt.Println(array)array[0] = 8array[1] = 9array[2] = 7fmt.Println(array)fmt.Println(array[2])
}
我们可以通过下标 0,1,2 来获取数组中的数据,下标 0,1,2 就表示数据的位置,我们也可以把指定位置的数据替换成另外一个数据。
数组这一数据类型,是被编程语言高度抽象封装的结构,下标 会转换成 虚拟内存地址,然后操作系统会自动帮我们进行寻址,这个寻址过程是特别快的,所以往数组的某个下标取一个值和放一个值,时间复杂度都为 O(1)。
它是一种将 虚拟内存地址 和 数据元素 映射起来的内置语法结构,数据和数据之间是挨着,存放在一连续的内存区域,每一个固定大小(8字节)的内存片段都有一个虚拟的地址编号。当然这个虚拟内存不是真正的内存,每个程序启动都会有一个虚拟内存空间来映射真正的内存,这是计算机组成的内容,和数据结构也有点关系,我们会在另外的高级专题讲,这里就不展开了。
数组是数据结构 顺序表 的具体实现。
用数组也可以实现链表,比如定义一个数组 [5]Value,值类型为一个结构体 Value ,该结构体有两个字段: 值 和 下一个节点的下标:
package mainimport "fmt"func ArrayLink() {type Value struct {Data stringNextIndex int64}var array [5]Value // 五个节点的数组array[0] = Value{"I", 3} // 下一个节点的下标为3array[1] = Value{"Army", 4} // 下一个节点的下标为4array[2] = Value{"You", 1} // 下一个节点的下标为1array[3] = Value{"Love", 2} // 下一个节点的下标为2array[4] = Value{"!", -1} // -1表示没有下一个节点node := array[0]for {fmt.Println(node.Data)if node.NextIndex == -1 {break}node = array[node.NextIndex]}
}
func main() {ArrayLink()
}
输出结果:
I
Love
You
Army
!
获取某个下标的数据,通过该数据可以知道下一个数据的下标是什么,然后拿出该下标的数据,继续往下做。问题是,有时候需要做删除,移动等各种操作,而数组的大小是固定的,需要大量空间移动,所以某些情况下,数组的效率很低。
数组(顺序表)和链表是两个不同的概念。一个是编程语言提供的基本数据类型,表示一个连续的内存空间,可通过一个索引访问数据。另一个是我们定义的数据结构,通过一个数据节点,可以定位到另一个数据节点,不要求连续的内存空间。
数组的优点是占用空间小,查询快,直接使用索引就可以获取数据元素,缺点是移动和删除数据元素要大量移动空间。
链表的优点是移动和删除数据元素速度快,只要把相关的数据元素重新链接起来,但缺点是占用空间大,查找需要遍历。
很多其他的数据结构都由数组和链表配合实现的。
五、总结
链表 和 数组 可以用来辅助构建各种基本数据结构。
数据结构名字特别多,在以后的计算机生涯中,有些自己造的数据结构,或者不常见的别人造的数据结构,不知道叫什么名字是很正常的。我们只需知道常见的数据结构即可,方便与其他程序员交流。
参考链接
- 链表
【数据结构】链表 - Go 语言实现相关推荐
- 用c语言实现链表数据保存,数据结构链表C语言实现.doc
数据结构链表C语言实现 数学与信息技术学院2016~2017(下)学年 计科专业2015级<数据结构>实验报告 2 学号:2015201018 姓名:汪继超 实验名称线性表的链式存储结构完 ...
- 图书信息管理系统(数据结构链表,c语言版)
图书信息管理系统 链表 一.实验题目 二.工具环境 三.实验问题 四.实验代码 五.实验总结 一.实验题目 图书信息管理系统 出版社有一些图书数据,为简单起见,在此假设每种图书只包括三部分信息:ISB ...
- c语言仓库管理系统链表,仓库管理系统 C语言 C++ 数据结构 链表 课程设计
仓库管理系统 C语言 C++ 数据结构 链表 课程设计 #include #include #include #include #define MAX 64 typedef struct node{ ...
- c语言仓库管理系统链表,仓库管理系统 C语言 C 数据结构 链表 课程设计.doc
仓库管理系统 C语言 C 数据结构 链表 课程设计 #include #include #include #include #define MAX 64 typedef struct node{ /* ...
- 数据结构(C语言版)学习笔记2-单链表
数据结构(C语言版)学习笔记2-单链表 1.单链表定义 typedef int ElemTypes; typedef struct node {ElemTypes data; //数据域struct ...
- 资料分享:送你一本《数据结构(C语言版)》电子书!
要想写出可复用.可扩展.易维护.灵活性好的代码,「数据结构」这一关必须要过啊! 在数据结构与算法的众多教材中,奉为经典的当属清华大学严蔚敏老师的著作.很多学校也选择这本书作为考研指定教材. 正在学习数 ...
- 资料分享:送你一本《数据结构(C#语言版)》电子书!
对于信息类专业的学生而言,数据结构与算法是一门必修的课程.只有学好这门课程,熟练掌握线性表.栈.队列.树.图等基本结构,以及在这些结构上的各种算法,才能利用计算机去解决实际问题. 如何学好这门课程呢, ...
- 数据结构(C语言版) 第 八 章 排序 知识梳理 + 习题详解
目录 一.归并排序 二.交换排序 1.快速排序 2.冒泡排序 三.插入排序 1.直接插入排序(基于顺序查找) 2.折半插入排序(基于折半查找) 3.希尔排序(基于逐趟缩小增量) 四.选择排序 0.直接 ...
- 数据结构(C语言版) 第 六 章 图 知识梳理 + 习题详解
目录 一. 图的基本定义和术语 一.图的基本概念 1.度 2.连通 (1)连通图 (2)强连通/强连通图 3.回路 4.完全图 二.图的三种存储结构 1.邻接矩阵表示法 2.邻接表(链式)表示法 3. ...
- 数据结构(C语言版) 第 三 章 栈与队列 知识梳理 + 作业习题详解
目录 一.栈 0.栈的基本概念 1.栈的实现 2.栈与递归 3.Hanoi塔问题 二.队列 0.队列的基本概念 1.队列的实现 2.循环队列 2.1循环队列的相关条件和公式: 3.链队列 4.链队列完 ...
最新文章
- 中国互联网+光伏建筑一体化行业商业模式创新与投资机会深度报告
- 龙岩学院计算机二级报名时间,福建龙岩学院2015年12月计算机等级考试报名通知...
- python文本替换 数据库_在Python中使用ASCII文件中的注释查找/替换子...
- Mybatis高级-resultMap之collection聚集
- [vue] SPA单页面的实现方式有哪些?
- Readhat中挂载yum源
- (转)学习淘淘商城第二十二课(KindEditor富文本编辑器的使用)
- 刚刚,贺建奎回应一切:如果是我孩子,我会第一个去试验
- 使用基于轮询的SQL数据缓存依赖
- 游牛音乐网源码/音乐网网站平台源码
- c语言延时跑马灯实验报告,跑马灯实验C语言程序
- 易语言群控雷电_安卓群控系统雷电模拟器安卓多开模拟器多开群控系统企业自动化的营销系统软件-资源下载随便下源码网...
- 基于Java+Springboot+vue体育用品销售商城平台设计和实现
- 手机虚拟键盘的获取按键的code值
- 英语之脆弱的,易受伤的
- LOJ10064黑暗城堡
- 极致体验,解密微信背后的音视频通话技术
- idea重启端口占用问题
- 华为虚拟服务器密码忘记怎么办,登录云服务器密码忘记了怎么办
- 599.两个列表的最小索引总和
热门文章
- wget通过代理下载之错误解决1(Proxy tunneling failed: Forwarding failureUnable to establish SSL connection.)
- pymongo 基本操作
- 一篇文章带你了解,App 测试工具
- spring框架_03
- Word全英文件怎么翻译
- snmpset对象不可写_写 I/O 路径 (FTT1/RF2) 对比 – Nutanix vs VMware vSAN
- 今日头条——青龙羊毛
- android和ios测试环境搭建,iOS自动化测试环境搭建
- 颜色恒常知觉的计算理论——Retinex理论
- php登陆界面点登陆没有反应,ThinkPHP5实战的登录界面的登陆按钮点击之后没有任何反应怎么解决?...