GO语言数据结构之链表
链表是一种物理存储单元上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的。链表由一系列结点(链表中每一个元素称为结点)组成,结点可以在运行时动态生成。每个结点包括两个部分:一个是存储数据元素的数据域,另一个是存储下一个结点地址的指针域。
单向链表
单链表是一种链式存取的数据结构,用一组地址任意的存储单元存放线性表中的数据元素。链表中的数据是以节点来表示的,每个结点的构成:数据 + 后续元素位置指针。
单项链表代码实现
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