示意图
注意
- 头节点有数据
- 最后一个节点的 next 指向 头节点 head, 构成一个环
- 删除节点
- 双指针,一前一后,前面的用来比较判断,后一个用来操作删除(单向的链表无法自删除,需要借助辅助节点)
- 只有一个节点,next 指向 nil
- 删除的是第一个节点,则需要重新修改 head 的指向
代码
// 环形单向链表package mainimport "fmt"type CatNode struct {no intname stringnext *CatNode}func insertNode(head *CatNode, newCatNode *CatNode) {// 1. 处理第一个节点(头节点有值)if head.next == nil {head.no = newCatNode.nohead.name = newCatNode.namehead.next = head // 构成环形fmt.Printf("第一个节点: 猫: %v 加入环形链表 \n", newCatNode.name)return}// 2. 处理别的节点// 找到最后的节点// 定义临时变量temp := headfor {if temp.next == head {break}temp = temp.next}// 加入到链表temp.next = newCatNodenewCatNode.next = head}// 按顺序插入func insertNode02(head *CatNode, newCatNode *CatNode) {fmt.Println("insertNode02 - head: ", head)// 1. 处理第一个节点(头节点有值)if head.next == nil {head.no = newCatNode.nohead.name = newCatNode.namehead.next = head // 构成环形fmt.Printf("第一个节点: 猫: %v 加入环形链表 \n", newCatNode.name)return}// 2. 处理别的节点// 定义临时变量temp := headflag := truefor {if temp.no == newCatNode.no {fmt.Printf("节点: %v , 已经存在,不允许加入", newCatNode.no)flag = falsebreak} 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 = falsebreak}temp = temp.next}// 加入到链表// temp.next = newCatNode// newCatNode.next = headif !flag {fmt.Println("不能插入节点")return} else {// 最后一个节点后插入if temp.next == head {temp.next = newCatNodenewCatNode.next = head} else {// 中间节点插入newCatNode.next = temp.nexttemp.next = newCatNode}}}// 删除节点func delNode(head *CatNode, id int) *CatNode {// 这里的头节点是有值的fmt.Println("delNode - head: ", head)temp := headhelper := 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 := truefor {if temp.next == head {// 只是比较,还没有执行删除操作break}if temp.no == id {if temp == head {// 删除的是头节点, 修改头节点的指向head = temp.next}helper.next = temp.nextflag = falsefmt.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 := headif 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")// 删除之后,重新制定 头节点 headhead = 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] =>删除 1delNode - 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 mainimport "fmt"type Boy struct {No intNext *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++ {// 生成 boyboy := &Boy{No: i,}if i == 1 {first = boycurBoy = boycurBoy.Next = first} else {curBoy.Next = boycurBoy = boycurBoy.Next = first}}return first}// 显示单向环形链表func show(first *Boy) {if first.Next == nil {fmt.Println("空链表")return}curBoy := firstfor {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 := firstfor {if tail.Next == first {break}tail = tail.Next}// 定位到初始节点 赋给first; tail 跟着移动到 first 后面for i := 1; i <= startNo-1; i++ {first = first.Nexttail = 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.Nexttail = tail.Next}// 2. 执行删除fmt.Printf("编号: %v 出列\n", first.No)first = first.Nexttail.Next = first// 向临界条件逼近 first.Next == first, 或者 first == tailif 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] =>*/




