Golang——实现约瑟夫问题

目录

问题介绍

约瑟夫问题解决

定义结构体

构建环形链表

显示环形链表

约瑟夫问题核心算法

整体代码

代码展示


问题介绍

约瑟夫问题是一个古老的数学问题,据说起源于公元1世纪。这个问题的故事情境是:在一个围成圆圈的人群中,依次数到某个数就将该人杀死,直到只剩下最后一个人。问题的关键是找出最后幸存的人的位置。

具体来说,假设有n个人围成一圈,从第k个人开始依次报数,报到m的人出局。然后从下一个人重新开始报数,报到m的人再次出局。重复这个过程,直到只剩下一个人。

约瑟夫问题解决

定义结构体

约瑟夫问题主要是使用环形链表进行实现。环形链表的基本讲解:Golang——实现环形链表-CSDN博客

定义People结构体,包括id ,以及下一个节点信息的next

type people struct {
	id   int
	next *people
}

构建环形链表

创建InputPeople方法传入要创建的节点个数,(也就是总人数,问题中的n个人)返回的是头节点首先判断输入的节点个数是否符合要求,如果符合要求则创建两个空节点用于辅助后面创建环形链表,通过for循环进行创建,因为第一个节点比较关键因此先将头节点进行自我形成环状,然后将其他节点添加进入环形链表中,最后返回头节点。

func InputPeople(num int) *people {
	first := &people{}  //空节点
	curcle := &people{} //空节点

	//	判断
	if num < 1 {
		fmt.Println("输入信息不符合要求,请重新输入")
		return first
	}

	//	循环的构建这个环形链表
	for i := 1; i <= num; i++ {
		people := &people{
			id: i,
		}
		//	第一个孩子比较特殊
		if i == 1 {
			first = people
			curcle = people
			curcle.next = first
		} else {
			curcle.next = people
			curcle = people
			curcle.next = first //构成环形链表
		}
	}
	return first
}

显示环形链表

定义辅助节点temp,判断环形链表是否为空,如果为空链表则退出,如果不为空通过for循环打印输出环形链表的各个节点。

func ShowPeople(people *people) {
	curcle := people
	if curcle.next == nil {
		fmt.Println("空链表")
		return
	}
	for {
		fmt.Printf("[%d]----> ", curcle.id)
		if curcle.next == people {
			break
		}
		curcle = curcle.next
	}
}

约瑟夫问题核心算法

  1. 编写一个函数,参数包括:first(头节点),startId(从第k个人开始查),countNum(查到第m个数).
  2. 首先判断是不是空链表,如果是则输出”空链表“,并退出程序。
  3. 如果不是,创建辅助节点tail,通过for循环让tail指向环形链表的最后一个节点,这样的话,first节点和tail节点就一直相近,通过tail节点进行删除,
  4. 通过for循环让first和tail移动starId次,这个时候first就指向第k个数,后面删除节点以first为准。(这里的”i<start-1“是因为查的时候就包括first本身)
  5. 这个时候就开始能查m个数了,通过for循环,让first和tail分别指向要删除的数和要删除的数的前一个数,将first指向他的下一个节点,tail的下一个节点指向first即可完成删除。
  6. for循环一直执行”5“这个操作,直到first==tail代表这环形链表只剩下最后一个节点,这个时候在for循环外面输出最后一个节点即可。
func Achieve(first *people, startId int, countNum int) {
	//	处理空链表
	if first.next == nil {
		fmt.Println("空链表")
		return
	}
	//	需要定义辅助指针
	tail := first
	for {
		if tail.next == first {
			break
		}
		tail = tail.next
	}
	//	让first移动到startId 次
	for i := 0; i < startId-1; i++ {
		first = first.next
		tail = tail.next
	}
	fmt.Println()
	//	开始数countNum,然后就删除first指向的节点
	for {
		//	开始数countNum-1次
		for i := 0; i < countNum-1; i++ {
			first = first.next
			tail = tail.next
		}
		fmt.Printf("节点为[%d]出列\n", first.id)
		//	删除first指向的节点
		first = first.next
		tail.next = first
		if first == tail {
			//	说明只剩一个节点
			break
		}
	}
	fmt.Println("最后出列的节点是:", first.id)
}

整体代码

package main

import "fmt"

type people struct {
	id   int
	next *people
}


// InputPeople 添加人员
func InputPeople(num int) *people {
	first := &people{}  //空节点
	curcle := &people{} //空节点

	//	判断
	if num < 1 {
		fmt.Println("输入信息不符合要求,请重新输入")
		return first
	}

	//	循环的构建这个环形链表
	for i := 1; i <= num; i++ {
		people := &people{
			id: i,
		}
		//	第一个孩子比较特殊
		if i == 1 {
			first = people
			curcle = people
			curcle.next = first
		} else {
			curcle.next = people
			curcle = people
			curcle.next = first //构成环形链表
		}
	}
	return first
}

// ShowPeople 显示单向环形链表
func ShowPeople(people *people) {
	curcle := people
	if curcle.next == nil {
		fmt.Println("空链表")
		return
	}
	for {
		fmt.Printf("[%d]----> ", curcle.id)
		if curcle.next == people {
			break
		}
		curcle = curcle.next
	}
}

func Achieve(first *people, startId int, countNum int) {
	//	处理空链表
	if first.next == nil {
		fmt.Println("空链表")
		return
	}


	//	需要定义辅助指针
	tail := first
	for {
		if tail.next == first {
			break
		}
		tail = tail.next
	}
	//	让first移动到startId 次,后面删除节点以first为准
	for i := 0; i < startId-1; i++ {
		first = first.next
		tail = tail.next
	}
	fmt.Println()
	//	开始数countNum,然后就删除first指向的节点
	for {
		//	开始数countNum-1次
		for i := 0; i < countNum-1; i++ {
			first = first.next
			tail = tail.next
		}
		fmt.Printf("节点为[%d]出列\n", first.id)
		//	删除first指向的节点
		first = first.next
		tail.next = first
		if first == tail {
			//	说明只剩一个节点
			break
		}
	}
	fmt.Println("最后出列的节点是:", first.id)
}

func main() {
	first := InputPeople(5)
	ShowPeople(first)
	Achieve(first, 2, 3)
}

代码展示

总共有5个人围成一个圈,从第2个人开始查3个数,第三个的人出列知道剩下最后一人

  • 28
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值