目录
问题介绍
约瑟夫问题是一个古老的数学问题,据说起源于公元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
}
}
约瑟夫问题核心算法
- 编写一个函数,参数包括:first(头节点),startId(从第k个人开始查),countNum(查到第m个数).
- 首先判断是不是空链表,如果是则输出”空链表“,并退出程序。
- 如果不是,创建辅助节点tail,通过for循环让tail指向环形链表的最后一个节点,这样的话,first节点和tail节点就一直相近,通过tail节点进行删除,
- 通过for循环让first和tail移动starId次,这个时候first就指向第k个数,后面删除节点以first为准。(这里的”i<start-1“是因为查的时候就包括first本身)
- 这个时候就开始能查m个数了,通过for循环,让first和tail分别指向要删除的数和要删除的数的前一个数,将first指向他的下一个节点,tail的下一个节点指向first即可完成删除。
- 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个数,第三个的人出列知道剩下最后一人