go语言数据结构第五篇-链表(单向环形链表)

单向环形链表应用场景

约瑟夫问题:

设编号为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)
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值