示意图
注意
- 头节点有数据
- 最后一个节点的 next 指向 头节点 head, 构成一个环
- 删除节点
- 双指针,一前一后,前面的用来比较判断,后一个用来操作删除(单向的链表无法自删除,需要借助辅助节点)
- 只有一个节点,next 指向 nil
- 删除的是第一个节点,则需要重新修改 head 的指向
代码
// 环形单向链表
package main
import "fmt"
type CatNode struct {
no int
name string
next *CatNode
}
func insertNode(head *CatNode, newCatNode *CatNode) {
// 1. 处理第一个节点(头节点有值)
if head.next == nil {
head.no = newCatNode.no
head.name = newCatNode.name
head.next = head // 构成环形
fmt.Printf("第一个节点: 猫: %v 加入环形链表 \n", newCatNode.name)
return
}
// 2. 处理别的节点
// 找到最后的节点
// 定义临时变量
temp := head
for {
if temp.next == head {
break
}
temp = temp.next
}
// 加入到链表
temp.next = newCatNode
newCatNode.next = head
}
// 按顺序插入
func insertNode02(head *CatNode, newCatNode *CatNode) {
fmt.Println("insertNode02 - head: ", head)
// 1. 处理第一个节点(头节点有值)
if head.next == nil {
head.no = newCatNode.no
head.name = newCatNode.name
head.next = head // 构成环形
fmt.Printf("第一个节点: 猫: %v 加入环形链表 \n", newCatNode.name)
return
}
// 2. 处理别的节点
// 定义临时变量
temp := head
flag := true
for {
if temp.no == newCatNode.no {
fmt.Printf("节点: %v , 已经存在,不允许加入", newCatNode.no)
flag = false
break
} else if temp.next == head {
break
} else if temp.next.no > newCatNode.no {
break
} else if temp.next.no == newCatNode.no {
fmt.Printf("节点: %v , 已经存在,不允许加入", newCatNode.no)
flag = false
break
}
temp = temp.next
}
// 加入到链表
// temp.next = newCatNode
// newCatNode.next = head
if !flag {
fmt.Println("不能插入节点")
return
} else {
// 最后一个节点后插入
if temp.next == head {
temp.next = newCatNode
newCatNode.next = head
} else {
// 中间节点插入
newCatNode.next = temp.next
temp.next = newCatNode
}
}
}
// 删除节点
func delNode(head *CatNode, id int) *CatNode {
// 这里的头节点是有值的
fmt.Println("delNode - head: ", head)
temp := head
helper := head // helper 指向 head 的上一个节点, 即尾节点
// 1. 空链表
if temp.next == nil {
fmt.Println("空链表")
return head
}
// 2. 只有一个
if temp.next == head {
temp.next = nil // 没有任何指向就会被gc回收,即删除
return head
}
// 3. 将 helper 指向最后
for {
if helper.next == head {
break
}
helper = helper.next
}
// 4. 两个及以上节点
flag := true
for {
if temp.next == head {
// 只是比较,还没有执行删除操作
break
}
if temp.no == id {
if temp == head {
// 删除的是头节点, 修改头节点的指向
head = temp.next
}
helper.next = temp.next
flag = false
fmt.Printf("猫: no: %v 被移除链表了 \n", id)
break
}
temp = temp.next // 移动 - 比较
helper = helper.next // 移动 - 执行删除
}
// 这里要增加一次比较
if flag {
if temp.no == id {
// temp 就是 要删除的节点
helper.next = temp.next
} else {
fmt.Println("没有找到要删除的节点id: ", id)
return head
}
}
return head
}
// 显示所有节点
func showNode(head *CatNode) {
temp := head
if temp.next == nil {
fmt.Println("空链表")
return
}
for {
fmt.Printf("[猫: no: %v, name: %v] => ", temp.no, temp.name)
if temp.next == head {
break
}
temp = temp.next
}
fmt.Println()
}
func main() {
// 初始化环形链表头节点
head := &CatNode{}
cat1 := &CatNode{
no: 1,
name: "tom1",
}
cat2 := &CatNode{
no: 2,
name: "tom2",
}
cat3 := &CatNode{
no: 3,
name: "tom3",
}
insertNode02(head, cat1)
insertNode02(head, cat3)
insertNode02(head, cat2)
insertNode02(head, cat3)
fmt.Println("显示")
showNode(head)
fmt.Println("删除 1")
// 删除之后,重新制定 头节点 head
head = delNode(head, 1)
showNode(head)
}
/*
insertNode02 - head: &{0 <nil>}
第一个节点: 猫: tom1 加入环形链表
insertNode02 - head: &{1 tom1 0xc00004e3c0}
insertNode02 - head: &{1 tom1 0xc00004e420}
insertNode02 - head: &{1 tom1 0xc00004e400}
节点: 3 , 已经存在,不允许加入不能插入节点
显示
[猫: no: 1, name: tom1] => [猫: no: 2, name: tom2] => [猫: no: 3, name: tom3] =>
删除 1
delNode - head: &{1 tom1 0xc00004e400}
猫: no: 1 被移除链表了
[猫: no: 2, name: tom2] => [猫: no: 3, name: tom3] =>
*/
👉约瑟夫问题
Josephu 问题
Josephu 问题为:设编号为 1,2,… n的n个人围坐一圈,约定编号为 k(1<=k<=n)的人从 1开始报数,数到 m 的那个人出列,它的下一位又从 1 开始报数,数到 m 的那个人又出列,依次类推,直到所有人出列为止,由此产生一个出队编号的序列。
提示
用一个不带头结点的循环链表来处理Josephu 问题:先构成一个有 n 个结点的单循环链表,然后由 k 结点起从 1 开始计数,计到 m 时,对应结点从链表中删除,然后再从被删除结点的下一个结点又从 1 开始计数,直到最后一个结点从链表中删除算法结束。
解题示意图:
代码实现
// 约瑟夫问题 (丢手绢), 单向环形链表
package main
import "fmt"
type Boy struct {
No int
Next *Boy
}
// 编写函数 构成 单向环形链表
// num 表示小孩的个数
// *Boy 返回该
func AddBoy(num int) *Boy {
first := &Boy{}
curBoy := &Boy{}
if num < 1 {
fmt.Println("num的值不对")
return first
}
for i := 1; i <= num; i++ {
// 生成 boy
boy := &Boy{
No: i,
}
if i == 1 {
first = boy
curBoy = boy
curBoy.Next = first
} else {
curBoy.Next = boy
curBoy = boy
curBoy.Next = first
}
}
return first
}
// 显示单向环形链表
func show(first *Boy) {
if first.Next == nil {
fmt.Println("空链表")
return
}
curBoy := first
for {
fmt.Printf("[编号: %v] => ", curBoy.No)
if curBoy.Next == first {
break
}
curBoy = curBoy.Next
}
fmt.Println()
}
// 游戏开始了, 结束后 first 重新赋值, 状态归位(否则会引发问题), 以便后续操作
func PlayGame(first *Boy, startNo int, countNum int) *Boy {
if first.Next == nil {
fmt.Println("空链表")
return first
}
// 定义辅助指针, 指向最后一个节点
tail := first
for {
if tail.Next == first {
break
}
tail = tail.Next
}
// 定位到初始节点 赋给first; tail 跟着移动到 first 后面
for i := 1; i <= startNo-1; i++ {
first = first.Next
tail = tail.Next
}
// for循环处理方式
// for {
// // 找到要删除的节点,
// for i := 1; i <= countNum-1; i++ {
// first = first.Next
// tail = tail.Next
// }
// // 执行删除
// fmt.Printf("编号: %v 出列\n", first.No)
// first = first.Next
// tail.Next = first
// // 可以编写回调函数
// if first == tail {
// break
// }
// }
// 以下是递归调用的方式
first = delNode(first, tail, countNum)
fmt.Printf("编号: %v 是最后一个\n", first.No)
return first
}
// 递归调用
func delNode(first *Boy, tail *Boy, countNum int) *Boy {
// 1. 找到要删除的节点,
for i := 1; i <= countNum-1; i++ {
first = first.Next
tail = tail.Next
}
// 2. 执行删除
fmt.Printf("编号: %v 出列\n", first.No)
first = first.Next
tail.Next = first
// 向临界条件逼近 first.Next == first, 或者 first == tail
if first.Next == first {
// 表示已经剩最后一个了,
return first
}
boy := delNode(first, tail, countNum)
// 返回最后剩下的
return boy
}
func main() {
first := AddBoy(5)
show(first)
fmt.Println("开始...")
first = PlayGame(first, 2, 3)
fmt.Println("------ 结束了, 显示剩余的:------")
show(first)
}
/*
[编号: 1] => [编号: 2] => [编号: 3] => [编号: 4] => [编号: 5] =>
开始...
编号: 4 出列
编号: 2 出列
编号: 1 出列
编号: 3 出列
编号: 5 是最后一个
------ 结束了, 显示剩余的:------
[编号: 5] =>
*/