数据结构之链表
阅读目录
一、链表介绍
二、单链表
三、双链表
四、环形链表
一、链表介绍
链表是有序的列表,在内存中存储如下:
二、单链表
func main() {
SingleLinkList()
}
// 链表 起名Node结尾
type HeroNode struct {
no int
name string
nickname string
next *HeroNode // 指向下一个节点
}
// 单链表的第一种方式
func SingleLinkList() {
// 1.先创建头节点
head := &HeroNode{}
// 2. 创建一个新的HeroNode
hero1 := &HeroNode{
no: 1,
name: "宋江",
nickname: "及时雨",
next: nil,
}
hero2 := &HeroNode{
no: 2,
name: "武松",
nickname: "不知道",
next: nil,
}
hero3 := &HeroNode{
no: 3,
name: "卢俊义",
nickname: "玉麒麟",
next: nil,
}
// 3.加入
//InsertHeroNode(head, hero1)
//InsertHeroNode(head, hero2)
//InsertHeroNode(head, hero3)
InsertHeroNode2(head, hero1)
InsertHeroNode2(head, hero2)
InsertHeroNode2(head, hero3)
//4.显示
ListHeroNode(head)
// 删除
fmt.Println()
DelHeroNode(head, 1)
DelHeroNode(head, 2)
DelHeroNode(head, 3)
DelHeroNode(head, 4)
fmt.Println()
ListHeroNode(head)
}
// 给列表增加or插入节点
// 第一种插入方式,在单链表最后插入
func InsertHeroNode(headNode *HeroNode, newNode *HeroNode) {
// 1.先找到该链表最后节点
// 2. 创建辅助节点[跑龙套]
tmp := headNode
for {
if tmp.next == nil {
//表示找到最后节点了
break
}
// tmp向下走
tmp = tmp.next
}
//3.加入newNode
tmp.next = newNode
}
// 给列表增加or插入节点
// 第二种插入方式,找到适当节点,插入,比如根据编号排序
func InsertHeroNode2(headNode *HeroNode, newNode *HeroNode) {
// 1.先找到该链表最后节点
// 2. 创建辅助节点[跑龙套]
tmp := headNode
for {
if tmp.next == nil {
//表示找到最后节点了
break
} else if tmp.next.no > newNode.no {
// newNode就应该插入tmp后面
break
} else if tmp.next.no == newNode.no {
fmt.Println("已存在no,插入错误!")
return
}
// tmp向下
tmp = tmp.next
}
//3.加入newNode
newNode.next = tmp.next // 先将加入节点的下一个节点指向tmp下一个节点
tmp.next = newNode // 在将tmp下一个节点指向newNode
}
// 显示链表的所有节点信息
func ListHeroNode(head *HeroNode) {
//1.创建辅助节点
tmp := head
//2.判断列表是否为空
if tmp.next == nil {
fmt.Println("空链表...")
return
}
//3.遍历列表
for {
fmt.Printf("节点信息:[%d, %s, %s, ] ->", tmp.next.no, tmp.next.name, tmp.next.nickname)
// 判断是否到了链表尾部
tmp = tmp.next
if tmp.next == nil {
break
}
}
}
// 删除节点
func DelHeroNode(head *HeroNode, id int) {
tmp := head
flag := false
for {
if tmp.next == nil {
break
} else if tmp.next.no == id {
// 找到了
flag = true
break
}
tmp = tmp.next
}
if flag {
// 改变连接,垃圾回收会处理单连
tmp.next = tmp.next.next
} else {
fmt.Println("删除的元素不存在...")
}
}
三、双链表
// 双向链表
func main() {
DoubleLinkList()
}
// 链表 起名Node结尾
type HeroNode1 struct {
no int
name string
nickname string
pre *HeroNode1 // 指向前一个节点
next *HeroNode1 // 指向下一个节点
}
// 单链表的第一种方式
func DoubleLinkList() {
// 1.先创建头节点
head := &HeroNode1{}
// 2. 创建一个新的HeroNode
hero1 := &HeroNode1{
no: 1,
name: "宋江",
nickname: "及时雨",
next: nil,
pre: nil,
}
hero2 := &HeroNode1{
no: 2,
name: "武松",
nickname: "不知道",
next: nil,
pre: nil,
}
hero3 := &HeroNode1{
no: 3,
name: "卢俊义",
nickname: "玉麒麟",
next: nil,
pre: nil,
}
// 3.加入
InsertHeroNode1(head, hero1)
InsertHeroNode1(head, hero2)
InsertHeroNode1(head, hero3)
//4.显示
ListHeroNode1(head)
// 删除
fmt.Println()
ListHeroNode2(head)
//DelHeroNode(head, 1)
//DelHeroNode(head, 2)
//DelHeroNode(head, 3)
//DelHeroNode(head, 4)
//fmt.Println()
//ListHeroNode(head)
}
// 给列表增加or插入节点
// 第一种插入方式,在单链表最后插入
func InsertHeroNode1(headNode *HeroNode1, newNode *HeroNode1) {
// 1.先找到该链表最后节点
// 2.创建辅助节点[跑龙套]
tmp := headNode
for {
if tmp.next == nil {
//表示找到最后节点了
break
}
// tmp向下走
tmp = tmp.next
}
//3.加入newNode
tmp.next = newNode
newNode.pre = tmp
}
// 给列表增加or插入节点
// 第二种插入方式,找到适当节点,插入,比如根据编号排序
func InsertHeroNode3(headNode *HeroNode1, newNode *HeroNode1) {
// 1.先找到该链表最后节点
// 2. 创建辅助节点[跑龙套]
tmp := headNode
for {
if tmp.next == nil {
//表示找到最后节点了
break
} else if tmp.next.no > newNode.no {
// newNode就应该插入tmp后面
break
} else if tmp.next.no == newNode.no {
fmt.Println("已存在no,插入错误!")
return
}
// tmp向下
tmp = tmp.next
}
//3.加入newNode
newNode.next = tmp.next // 先将加入节点的下一个节点指向tmp下一个节点
newNode.pre = tmp
if tmp.next != nil {
tmp.next.pre = newNode
}
tmp.next = newNode // 在将tmp下一个节点指向newNode
}
// 显示链表的所有节点信息
func ListHeroNode1(head *HeroNode1) {
//1.创建辅助节点
tmp := head
//2.判断列表是否为空
if tmp.next == nil {
fmt.Println("空链表...")
return
}
//3.遍历列表
for {
fmt.Printf("节点信息:[%d, %s, %s, ] ->", tmp.next.no, tmp.next.name, tmp.next.nickname)
// 判断是否到了链表尾部
tmp = tmp.next
if tmp.next == nil {
break
}
}
}
// 删除节点
func DelHeroNode1(head *HeroNode1, id int) {
tmp := head
flag := false
for {
if tmp.next == nil {
break
} else if tmp.next.no == id {
// 找到了
flag = true
break
}
tmp = tmp.next
}
if flag {
// 改变连接,垃圾回收会处理单连
tmp.next = tmp.next.next
if tmp.next != nil {
tmp.next.pre = tmp
}
} else {
fmt.Println("删除的元素不存在...")
}
}
// 倒序打印
// 显示链表的所有节点信息
func ListHeroNode2(head *HeroNode1) {
//1.创建辅助节点
tmp := head
//2.判断列表是否为空
if tmp.next == nil {
fmt.Println("空链表...")
return
}
for {
if tmp.next == nil {
break
}
// 走到最后
tmp = tmp.next
}
//3.遍历列表
for {
fmt.Printf("节点信息:[%d, %s, %s, ] ->", tmp.no, tmp.name, tmp.nickname)
// 判断是否到了链表尾部
tmp = tmp.pre
if tmp.pre == nil {
break
}
}
}
四、环形链表
单向环形链表
type CatNode struct {
no int
name string
next *CatNode
}
//插入环形链表
func InsertCatNode(head *CatNode, newNode *CatNode) {
if head.next == nil {
head.no = newNode.no
head.name = newNode.name
head.next = head // 构成环形
return
}
tmp := head
for {
if tmp.next == head {
break
}
tmp = tmp.next
}
tmp.next = newNode
newNode.next = head
}
func ListCircleLink(head *CatNode) {
tmp := head
if tmp.next == nil {
fmt.Println("空的环形链表...")
return
}
for {
fmt.Println("cat info:", tmp.name, "->")
if tmp.next == head {
break
}
tmp = tmp.next
}
}
func main() {
head := &CatNode{}
cat1 := &CatNode{
no: 1,
name: "tom1",
next: nil,
}
cat2 := &CatNode{
no: 2,
name: "tom2",
next: nil,
}
cat3 := &CatNode{
no: 3,
name: "tom3",
next: nil,
}
InsertCatNode(head, cat1)
InsertCatNode(head, cat2)
InsertCatNode(head, cat3)
ListCircleLink(head)
head = DelCatNode(head, 1)
fmt.Println()
ListCircleLink(head)
head = DelCatNode(head, 30)
fmt.Println()
ListCircleLink(head)
head = DelCatNode(head, 3)
fmt.Println()
ListCircleLink(head)
}
func DelCatNode(head *CatNode, no int) *CatNode {
tmp := head //辅助查找 no
helper := head // 标志最后位置
if tmp.next == nil {
fmt.Println("空链表...")
return head
}
// 如果只有一个节点
if tmp.next == head {
tmp.next = nil
return head
}
// 将helper指向最后
for {
if helper.next == head {
break
}
helper = helper.next
}
flag := true
//如果有两个及以上
for {
if tmp.next == head {
// 已经到最后一个 还没有比较/还没找到
break
}
if tmp.no == no {
if tmp == head {
// 删除头节点
head = head.next
}
// 正好匹配到
helper.next = tmp.next
flag = false
break
}
tmp = tmp.next //比较
helper = helper.next //一旦找到,helper辅助删除
}
if flag {
//整个循环中没有删除过,比较最后一次
if tmp.no == no {
// 比较最后一个
helper.next = tmp.next
} else {
fmt.Println("没有此节点...")
}
}
return head
}
单向环形链表应用 - josephu(丢手帕)
func main() {
first := AddBoy(500)
//显示
ShowBoy(first)
fmt.Println()
PlayGame(first, 3, 20)
}
type Boy struct {
no int //编号
next *Boy //指向下一个小孩指针
}
func AddBoy(num int) *Boy {
// 传入创建小孩的个数 返回第一个小孩的指针
first := &Boy{} //空节点 头
curBoy := &Boy{} //辅助节点
if num < 1 {
fmt.Println("不支持构建数据")
return first // 返回空指针
}
for i := 1; i <= num; i++ {
boy := &Boy{
no: i,
}
//1.因为第一个小孩比较特殊
if i == 1 {
first = boy
curBoy = boy
curBoy.next = first // 构成循环
} else {
curBoy.next = boy // 指针移位
curBoy = boy
curBoy.next = first //构成环形
}
}
return first
}
// 显示单向环形链表
func ShowBoy(head *Boy) {
//如果环形列表为空
if head.next == nil {
fmt.Println("空链表...")
}
// 辅助指针
curBoy := head
for {
fmt.Printf("小孩编号【%d】->", curBoy.no)
if curBoy.next == head {
break // 遍历完了
}
curBoy = curBoy.next // 下移
}
}
func PlayGame(first *Boy, startNo int, countNum int) {
//1.空的链表单独处理
if first.next == nil {
fmt.Println("空链表...")
return
}
// 2.start不能大于小孩总数
if startNo > 500 {
fmt.Println("不支持的开始节点")
return
}
// 3.需要定义赋值指针
tail := first //帮助删除
for {
if tail.next == first {
break // 指向最后一个节点
}
tail = tail.next
}
// 4.先让first移动到startNo
for i := 1; i <= startNo-1; i++ {
first = first.next
tail = tail.next //一起移动
}
// 5.开始数countNum,删除first指向的小孩
for {
if first == tail {
//判断退出条件
fmt.Println("最后出圈小孩:", first.no)
break
}
//数countNum-1
for i := 1; i <= countNum-1; i++ {
first = first.next
tail = tail.next //一起移动
}
fmt.Printf("第【%d】小孩出圈\n", first.no)
// 删除first
first = first.next //走到下一步
tail.next = first //断开连接原始first
}
}