单向环形链表应用场景
约瑟夫问题:
设编号为1,2,3,....,n的n个人围做一圈,约定编号为k(1<=k<=n)的人从1开始报数,数到m的那个人出列,它的下一位又从1开始报数,数到m的那个人又出列,以此类推,直到所有人出列为止,由此产生一个出队编号的序列。
思路:用一个不带头节点的单向环形链表,先构成一个有n个节点的单向循环链表,然后由k节点起从1开始计数,将计到m的那个节点从链表中删除,然后再从被删除的节点的下一个节点又从1开始计数,直到最后一个节点从链表中删除,算法结束。
单向环形链表如下所示:
假设有5个小孩玩约瑟夫问题游戏,过程如下:
图一 图二
代码如下:
package main
import "fmt"
//小孩的结构体
type Boy struct {
no int //编号
next *Boy //指向下一个小孩的指针,默认值nil
}
//编写一个函数,构成单向的环形链表
//num:表示小孩的个数;*Boy:返回该环形的链表的第一个小孩的指针
func addBoy(num int) *Boy {
first := &Boy{} //空节点,first指针,用于指向头节点
currentBoy :=&Boy{}//空节点,辅助指针,用于指向正在数数的小孩
if num < 1{
fmt.Println("小孩的数量至少要有一个")
return first
}
//循环的构建这个环形链表
for i := 1;i <= num ;i++ {
boy :=&Boy{
no:i,
}
//1.因为第一个小孩比较特殊
if i==1{
first =boy//头指针指向第一个小孩,first指针不能动,不然就找不到环形链表的入口了,因此还需要一个辅助指针
currentBoy =boy//辅助指针指向第一个小孩
currentBoy.next = first //只有一个小孩的环形链表
}else{
currentBoy.next = boy//将第二个小孩加入到链表中
currentBoy =boy//辅助指针指向第二个小孩
currentBoy.next = first //指向第二个小孩的辅助指针的next指向first,形成环形链表
}
}
return first
}
//显示单向的环形链表[遍历]
func showBoy(first *Boy) {
//处理一些如果环形链表为空
if first.next == nil{
fmt.Println("链表为空")
return
}
//创建一个指针,帮助遍历,说明至少有一个小孩
currentBoy := first
for {
fmt.Printf("小孩编号=%d ->",currentBoy.no)
//退出条件
if currentBoy.next == first{
break
}
currentBoy=currentBoy.next//currentBoy指针后移
}
}
//k=startNo;m=countNum
func playGame(first *Boy,startNo int,countNum int) {
//1.判断是否空链表
if first.next == nil{
fmt.Println("空链表")
return
}
//2.判断startNo <= 小孩的总数
//3.设置一个辅助指针,帮助删除环形链表中的小孩
assist :=first
//辅助指针需要指向环形链表的最后一个小孩,这个非常重要,删除小孩时需要用到
for {
if assist.next == first {//说明assist到了最后一个小孩了
break
}
assist =assist.next
}
//4.让first指针移动到startNo
for i :=1;i<= startNo - 1;i++ {
//first节点和辅助节点assist一起向前移动相同步数
first = first.next
assist = assist.next
}
//5.开始数countNum下,然后删除first指向的小孩,此时的first指向谁,就删掉哪个小孩
for{
//开始数countNum,即移动countNum-1个步数
for i :=1;i<= countNum - 1;i++ {
//first节点和辅助节点assist一起向前移动相同步数
first = first.next
assist = assist.next
}
fmt.Printf("小孩编号为%d出圈\n",first.no)
//删除first指向的节点
first = first.next //first往前移动一步
assist.next = first//删除节点
//如果assist=first,说明圈中只有一个小孩了
if assist == first{
break
}
}
fmt.Printf("最后出圈的小孩编号为%d\n",first.no)
}
func main() {
first :=addBoy(50)
showBoy(first)
fmt.Println()
playGame(first,20,13)
}