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

单向链表

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

单项链表代码实现

package main

import "fmt"

//定义链表节点结构
type singleNode struct {
no int
name string
next *singleNode //指向下一个数据地址,没有则为nil
} //定义链表结构
type singLinked struct {
head *singleNode // 链表头数据
tail *singleNode // 链表尾数据
size int //长度
} // InsertTail 从尾部插入数据
func (singLinked *singLinked) InsertTail(linked *singleNode) {
//数据校验
if linked == nil {
fmt.Println("数据为空")
return
}
//若链表长度为0
if singLinked.size == 0 {
//直接将数据插入头
singLinked.head = linked
singLinked.tail = linked
//最后一个节点next为nil
linked.next = nil
} else {
//如果存在有数据,将数据插入到上一个数据的尾部
singLinked.tail.next = linked
//并将插入的数据next置为nil
linked.next = nil
//尾部数据录入
singLinked.tail = linked
}
singLinked.size++
} // InterHead 从头部插入数据
func (singLinked *singLinked) InterHead(linked *singleNode) {
//数据校验
if linked == nil {
fmt.Println("数据为空")
return
}
//判断链表内是否存在数据
if singLinked.size == 0 {
singLinked.head = linked
singLinked.tail = linked
linked.next = nil
} else {
//如果有数据
head := singLinked.head
linked.next = head
singLinked.head = linked
}
singLinked.size++
} // InterNumber 根据编号大小进行插入排序
func (singLinked *singLinked) InterNumber(linked *singleNode) {
//数据校验
if linked == nil {
fmt.Println("数据为空")
return
}
//创建辅助数据
temp := singLinked.head
for {
if singLinked.size == 0 {
//链表为空直接插入
singLinked.head = linked
singLinked.tail = linked
linked.next = nil
singLinked.size++
break
} else if temp.next == nil {
//标识链表已到最后
//表示最后一位了
temp.next = linked
singLinked.tail = linked
singLinked.size++
break
} else if temp.no == linked.no {
//当前节点的下一个节点编号大于需要插入的数据编号
fmt.Println("编号冲突")
break
} else if linked.no < singLinked.head.no {
//判断是否需要插入头节点
linked.next = singLinked.head
singLinked.head = linked
singLinked.size++
break
} else if temp.next.no > linked.no {
//判断当前的数据编号是否比插入数据的编号大
linked.next = temp.next
temp.next = linked
singLinked.size++
break
}
//循环
temp = temp.next
}
} // GetHead 获取头节点
func (singLinked *singLinked) GetHead() *singleNode {
return singLinked.head
} // Print 遍历读取数据操作
func (singLinked *singLinked) Print() {
fmt.Printf("当前链表内有%v个数据\n", singLinked.size)
if singLinked.size == 0 {
fmt.Println("链表为空")
return
}
head := singLinked.GetHead()
for head != nil {
fmt.Printf("编号:%v 名称:%v\n", head.no, head.name)
head = head.next
}
} // DeleteNo 根据编号删除节点
func (singLinked *singLinked) DeleteNo(id int) {
//数据校验
if singLinked.size == 0 {
fmt.Println("数据异常")
return
}
//创建辅助节点
temp := singLinked.head
for {
if singLinked.head.no == id {
//是头节点,删除头结点
singLinked.head = singLinked.head.next
singLinked.size--
break
} else if temp.next.no == id {
//不是头节点,删除节点数据
temp.next = temp.next.next
singLinked.size--
break
} else if temp.next == nil {
//已经到最后了,并未找到该编号节点
fmt.Println("未找到数据")
break
}
temp = temp.next
}
} // Inquire 根据编号查询节点
func (singLinked *singLinked) Inquire(id int) (singleNode *singleNode) {
//数据校验
if singLinked.size == 0 {
fmt.Println("数据异常")
return
}
//创建辅助节点
temp := singLinked.head
for {
if singLinked.head.no == id {
//是头节点返回头结点
fmt.Println()
singleNode = singLinked.head
break
} else if temp.next.no == id {
//不是头节点,删除节点数据
singleNode = temp.next
break
} else if temp.next == nil {
//已经到最后了,并未找到该编号节点
fmt.Println("未找到数据")
break
}
temp = temp.next
}
return
} // Modification 修改指定id节点
func (singLinked *singLinked) Modification(id int, singleNode *singleNode) {
//数据校验
if singLinked.size == 0 || singleNode == nil {
fmt.Println("数据异常")
return
}
//创建辅助节点
temp := singLinked.head
for {
if temp.next == nil {
//已经到最后了,并未找到该编号节点
fmt.Println("未找到数据")
break
} else if singLinked.head.no == id {
//是头结点
singLinked.head.name = singleNode.name
break
} else if temp.next.no == id {
temp.next.name = singleNode.name
break
}
temp = temp.next
}
return
} // Destroy 链表销毁
func (singLinked *singLinked) Destroy() {
singLinked.head = nil
singLinked.tail = nil
singLinked.size = 0
} //主函数
func main() {
//初始化
linked := singLinked{}
fmt.Println("==============尾插数据分割线===============")
//添加操作
linked.InsertTail(&singleNode{
no: 7,
name: "娃哈哈",
})
linked.InsertTail(&singleNode{
no: 8,
name: "营养快线",
next: nil,
})
linked.Print()
fmt.Println("==============编号插入排序分割线===============")
linked.InterNumber(&singleNode{
no: 1,
name: "康师傅",
next: nil,
})
linked.InterNumber(&singleNode{
no: 9,
name: "康师傅",
next: nil,
})
linked.Print()
fmt.Println("==============根据编号查询分割线===============")
inquire := linked.Inquire(8)
fmt.Println(inquire)
fmt.Println("==============修改数据分割线===============")
linked.Modification(8, &singleNode{name: "嘻嘻哈哈"})
linked.Print()
fmt.Println("==============根据id删除数据分割线===============")
linked.DeleteNo(9)
linked.DeleteNo(1)
linked.DeleteNo(8)
linked.DeleteNo(7)
linked.Print()
}

双向链表

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

双向链表代码实现

package main

import "fmt"

// Node 节点结构
type Node struct {
no int
name string
front *Node //前节点
queen *Node //后节点
} // Linked 链表结构
type Linked struct {
size int //节点个数
head *Node //头节点
trail *Node //尾结点
} // InsertTrail 尾插法
func (linked *Linked) InsertTrail(node *Node) {
//数据校验
if node == nil {
fmt.Println("数据异常")
return
}
temp := linked.head
for true {
if temp.queen == nil {
break
}
temp = temp.queen
}
//找到最后一个节点将新节点插入最后
temp.queen = node
node.front = temp
linked.size++
} // InsertNumber 按照编号大小顺序插入
func (linked *Linked) InsertNumber(node *Node) {
//数据校验
if node == nil {
fmt.Println("数据异常")
return
}
temp := linked.head
for true {
if temp.queen == nil {
//遍历到最后,数据应插入最后
temp.queen = node
linked.trail = node
linked.size++
break
} else if temp.no == node.no {
fmt.Printf("数据ID冲突")
break
} else if temp.queen.no > node.no {
//下一个节点的id比插入节点id大
//插入逻辑,先将插入节点上指针和下指针分别指向上下节点再进行上下节点关联新增节点步骤
node.queen = temp.queen
node.front = temp
//判断是否还有下个节点
if temp.queen != nil {
temp.queen.front = node
}
temp.queen = node
linked.size++
break
}
temp = temp.queen
}
} // DeleteNode 删除节点根据id删除
func (linked *Linked) DeleteNode(id int) {
temp := linked.head
for true {
if temp.queen == nil {
fmt.Println("未找到该id节点")
break
} else if temp.queen.no == id {
temp.queen = temp.queen.queen
//判断是否还有下个节点
if temp.queen != nil {
temp.queen.front = temp
}
linked.size--
break
}
temp = temp.queen
}
} // InquireLinked 查询链表所有数据
func (linked *Linked) InquireLinked() {
fmt.Printf("当前链表内有%v个数据\n", linked.size)
if linked.size == 0 {
fmt.Println("链表为空")
}
temp := linked.head
for temp.queen != nil {
fmt.Printf("编号:%v 名称:%v\n", temp.queen.no, temp.queen.name)
temp = temp.queen
}
} // InversePrinting 倒序打印
func (linked *Linked) InversePrinting() {
if linked.size == 0 {
fmt.Println("链表为空")
}
temp := linked.trail
for true {
fmt.Printf("编号:%v 名称:%v\n", temp.no, temp.name)
temp = temp.front
if temp.front == nil {
break
}
}
} func main() {
//初始化
linked := Linked{
head: &Node{
no: 0,
name: "头结点",
},
trail: nil,
} linked.InsertTrail(&Node{
no: 2,
name: "王老吉",
})
linked.InsertNumber(
&Node{
no: 4,
name: "加多宝",
front: nil,
queen: nil,
})
linked.InsertNumber(
&Node{
no: 3,
name: "可口可乐",
front: nil,
queen: nil,
})
linked.InsertNumber(
&Node{
no: 1,
name: "雪碧",
front: nil,
queen: nil,
})
linked.DeleteNode(5)
linked.InquireLinked()
fmt.Println("=========分割线===========")
linked.InversePrinting()
}

环形链表

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

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

环形链表代码实现

package main

import "fmt"

//定义节点结构体
type Node struct {
no int
name string
next *Node //下一个节点
} // AnnularLinked 环形链表结构
type AnnularLinked struct {
size int
head *Node //头结点
tail *Node // 尾结点
} // Insertion 添加数据
func (linked *AnnularLinked) Insertion(node *Node) {
//判断链表是否有数据
if linked.size == 0 {
linked.head = node
linked.head.next = node
linked.tail = node
linked.size++
return
}
//如果有数据
linked.tail.next = node
node.next = linked.head
linked.tail = node
linked.size++
} // Print 打印循环链表
func (linked *AnnularLinked) Print() {
fmt.Printf("当前链表内有%v个数据\n", linked.size)
if linked.size == 0 {
fmt.Println("链表为空")
return
}
temp := linked.head
for {
fmt.Printf("编号:%v 名称:%v\n", temp.no, temp.name)
if temp.next == linked.head {
break
}
temp = temp.next
}
} // DeleteNode 删除某个节点
func (linked *AnnularLinked) DeleteNode(id int) {
//判断链表是否为空
if linked.size == 0 {
fmt.Println("空链表,无法执行删除")
return
}
//判断如果是删除头节点
if linked.head.no == id {
linked.tail.next = linked.head.next
linked.head = linked.head.next
linked.size--
return
}
temp := linked.head
for true {
if temp.next.no == id {
//如果删除的是尾节点
if temp.next == linked.tail {
linked.tail = temp
}
temp.next = temp.next.next
linked.size--
break
} else if temp.next == linked.head {
fmt.Println("未找到该id节点")
break
}
temp = temp.next
}
} func main() {
//初始化
linked := AnnularLinked{
size: 0,
head: &Node{},
tail: nil,
}
linked.Insertion(&Node{
no: 1,
name: "嘻嘻嘻",
next: nil,
})
linked.Insertion(&Node{
no: 2,
name: "嘻嘻嘻",
next: nil,
})
linked.Insertion(&Node{
no: 3,
name: "嘻嘻嘻",
next: nil,
})
linked.Insertion(&Node{
no: 4,
name: "嘻嘻嘻",
next: nil,
})
linked.DeleteNode(2)
linked.DeleteNode(1)
linked.DeleteNode(3)
linked.DeleteNode(4)
linked.Print()
}

本文借鉴:通俗易懂讲解 链表 - 知乎 (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. Visual Studio Code (VSCode) 配置 C/C++ 开发编译环境

    前言 工作多年,突然发现很多C++的基础都忘记了,加之C++不断更新换代后的各种新特性,于是想重拾C++的基础学习.虽然现在工作都是Linux平台,但考虑到个人方便,自己也仅仅想重温语法,家里家外都可 ...

  2. WPF实现统计图(饼图仿LiveCharts)

    WPF开发者QQ群: 340500857  | 微信群 -> 进入公众号主页 加入组织 每日一笑 下班和实习生一起回家,公交站等车,一乞丐把碗推向实习生乞讨.这时,实习生不慌不忙的说了句:&qu ...

  3. 最详细STL(一)vector

    vector的本质还是数组,但是可以动态的增加和减少数组的容量(当数组空间内存不足时,都会执行: 分配新空间-复制元素-释放原空间),首先先讲讲vector和数组的具体区别 一.vector和数组的区 ...

  4. Linux下Electron loadURL报错 ERR_FAILED(-2) Not allowed to load local resource

    Linux下Electron loadURL报错 ERR_FAILED(-2) Not allowed to load local resource 背景 使用electron-vue的时候,窗体创建 ...

  5. 题解 Christmas Game

    题目传送门 题目大意 给出 \(t\) 个 \(n\) 个点 \(m\) 条边的无向图,每次可以从任意一棵树选择一条边删掉,然后该树不与根(为 \(1\) )联通的部分被删掉.不能操作的人输.问谁有必 ...

  6. iOS能否自动扫描周边wifi信息并通过密码连接

    能否获取系统wifi列表信息 不能,只能获取用户当前连接的wifi信息 https://developer.apple.com/forums/thread/112177 https://develop ...

  7. Arp欺骗和DNS投毒的实验性分析

    1.中间人攻击之Arp欺骗/毒化 本文涉及网络安全攻击知识,随时可能被永久删除.请Star我的GitHub仓库 实现原理: 这种攻击手段也叫做中间人攻击MITM(Man-in-the-Middle) ...

  8. 力扣 - 剑指 Offer 39. 数组中出现次数超过一半的数字

    题目 剑指 Offer 39. 数组中出现次数超过一半的数字 思路1(排序) 因为题目说一定会存在超过数组长度一半的一个数字,所以我们将数组排序后,位于length/2位置的一定是众数 代码 clas ...

  9. LiveVideoStackCon2021 北京站专访:从上云到创新,视频云的新技术、新场景

    伴随着视频技术的进步和标准的迭代,视频产业从模拟进入到数字时代,完成了从电影电视到互联网的媒介转换,并且衍生出了超高清.3D.AR/VR 等多种创新形态.特别是在后疫情的当下,我们可以看到音视频技术领 ...

  10. JAVA的array中indexOf

    记得龙哥有个重构的文章里说直接判断啥的. 今天看JDK ArrayList,看到了他的 indexOf,他先判断,后进入循环,看似写了两遍for 循环,但是简单明了暴力.i like it . pub ...