约瑟夫问题(Josephus problem)详解
约瑟夫问题
1)设编号为 1,2,3 ... n 的 n 个人围坐一圈。
2)约定编号为 k (1 <= k <= n)的人从 1 开始报数,数到 m 的那个人出列。
3)它的下一位又从 1 开始报数,数到 m 的那个人又出列,依此类推,直到所有人出列为止。
4)由此产生一个出列编号的序列。
解题思路
1)构建一个结构体 Boy
No 为 Boy 的编号。
Next 指针指向下一个 Boy。
代码如下:
// 小孩的结构体
type Boy struct {No int // 编号Next *Boy // 指向下一个小孩的指针
}
2)根据编号,创建一个单向环形队列。
假设 n = 5 ,即一共圈内有5个小孩,创建完成的结构体如图所示:
first 指针指向队首。
curBoy 指针指向当前要添加的新节点。
当只有一个小孩时,构成一个自循环,Next 指针指向自身,如图所示:
当不止一个小孩,新节点加入的流程如图所示:
1、构建编号为 n 的新节点 boy
boy := &Boy{No: i,
}
2、循环加入编号为 n 的新节点:
让 curBoy 的 Next 指针指向新建立的节点 boy
让 curBoy 指针指向新建立的节点 boy
让 curBoy 的 Next 指针指向头节点 first
依此类推,循环加入新节点
如图所示:
循环构建单向环形链表的代码:
// 小孩构成单向环形链表
// num:小孩的个数
// *Boy:返回单向环形链表的头指针
func AddBoy(num int) *Boy {// 第一个小孩,空节点first := &Boy{}// 因为 first 指针固定指向第一个小孩// 因此 first 指针不能移动,需要一个辅助指针// 辅助指针 curBoy ,指向当前的小孩curBoy := &Boy{}if num < 1 {fmt.Println("num 的值有误")return first}// 循环地构成单向环形链表for i := 1; i <= num; i++ {boy := &Boy{No: i,}// 第一个小孩if i == 1 {first = boycurBoy = boy// 形成一个自循环curBoy.Next = first} else {curBoy.Next = boycurBoy = boy// 最后一个节点指向头节点// 形成一个单向环形链表curBoy.Next = first}}return first}
3)根据编号构建好单向环形队列后,编写一个出列算法:
1、让头指针 first 指向单向环形队列的头部。
2、让尾指针 tail 指向单向环形队列的尾部。
如图所示:
3、当 first 指针移动时,tail 指针也跟着移动相应的次数。
设置 tail 指针的目的是作为辅助指针,用来删除指定的节点。
假设:n = 5, k = 2 , m = 3 (即5个小孩围成一圈,由第2个小孩开始报数,数到3的小孩出列)。
未报数前:
由于是第 2 个小孩开始报数,因此 first 指针移动到节点 2 。
因为 tail 指针移动的次数和 first 指针一致,因此 tail 指针移动到节点 1。
如下图所示:
报数后:
由第2个小孩由1开始报数,数到 3 的小孩出列。
因此,报数后,first 指针移动到节点4。(节点2:报1,节点3:报2,节点4:报3)
tail 指针移动的次数和 first 指针一致,因此 tail 指针移动到节点 3。
如下图所示:
那么,节点4 出列。因此需要在单向环形链表上删除节点4。
因此,需要执行如下流程:
1.把 first 指针后移。
2.把 tail 指针的 Next 指向 first。
如下图所示:
由于 节点4 指向 节点5 ,但是没有任何节点指向 节点4 , 因此 节点4 会自动被系统的垃圾回收机制回收。
因此新构成的单向环形链表如图所示:
依此类推,循环出列节点,直到单向环形队列中只剩下一个节点。
循环出列结束的标志为 first == tail (因为当 first == tail 的时候,说明队列中只剩下了一个节点)
如图所示:
出列的算法如下:
// 解决约瑟夫问题的算法
// k : 约定编号为 k 的人从 1 开始报数
// m : 从1开始报数,数到 m 的人出列
func Josephu(first *Boy, k int, m int) {fmt.Println("\n小孩出圈的顺序如下:")// 单独处理空链表if first.Next == nil {fmt.Println("empty link list")return}if k > CountBoy(first) {fmt.Printf("输入的参数 %d 有误\n", k)return}// 定义一个辅助指针,帮助删除小孩tail := first// 让 tail 指向环形链表的最后一个节点(小孩)// 后面 tail 和 first 都往后挪动的时候// 才能保证 tail 一直在 fist 后面for {// 说明已经指向了最后一个节点// 退出循环if tail.Next == first {break}tail = tail.Next}// 让 first 移动到 k (编号为 k 的人开始报数)// 移动到编号 k ,只需要移动 k - 1 次for i := 0; i < k-1; i++ {first = first.Nexttail = tail.Next}// 开始数 m 下(数到 m 的人出列)// 然后就删除 first 指向的小孩for {for i := 0; i < m-1; i++ {first = first.Nexttail = tail.Next}fmt.Printf("编号为 %d 的小孩出圈\n", first.No)// 删除 first 指向的小孩first = first.Nexttail.Next = first// 如果圈中只有一个小孩if first == tail {fmt.Printf("编号为 %d 的小孩出圈\n", first.No)break}}}
完整代码
/*数据结构约瑟夫问题(Josephus problem):1)设编号为 1,2,3 ... n 的 n 个人围坐一圈2)约定编号为 k (1 <= k <= n)的人从 1 开始报数数到 m 的那个人出列3)它的下一位又从 1 开始报数,数到 m 的那个人又出列依此类推,直到所有人出列为止4)由此产生一个出列编号的序列
*/
package mainimport "fmt"// 小孩的结构体
type Boy struct {No int // 编号Next *Boy // 指向下一个小孩的指针
}// 小孩构成单向环形链表
// num:小孩的个数
// *Boy:返回单向环形链表的头指针
func AddBoy(num int) *Boy {// 第一个小孩,空节点first := &Boy{}// 因为 first 指针固定指向第一个小孩// 因此 first 指针不能移动,需要一个辅助指针// 辅助指针 curBoy ,指向当前的小孩curBoy := &Boy{}if num < 1 {fmt.Println("num 的值有误")return first}// 循环地构成单向环形链表for i := 1; i <= num; i++ {boy := &Boy{No: i,}// 第一个小孩if i == 1 {first = boycurBoy = boy// 形成一个自循环curBoy.Next = first} else {curBoy.Next = boycurBoy = boy// 最后一个节点指向头节点// 形成一个单向环形链表curBoy.Next = first}}return first}// 显示单向的环形链表
func ShowBoy(first *Boy) {// 单独处理空链表if first.Next == nil {fmt.Println("empty link list")return}// 创建一个辅助指针,帮助遍历curBoy := firstfor {fmt.Printf("小孩编号 = %d -> ", curBoy.No)// 退出循环的条件// 单向环形链表的最后一个节点指向头指针if curBoy.Next == first {break}curBoy = curBoy.Next}}// 统计小孩的数量
func CountBoy(first *Boy) int {// 单独处理空链表if first.Next == nil {return 0}// 创建一个辅助指针,帮助遍历curBoy := first// 计数count := 0for {count++// 退出循环的条件// 单向环形链表的最后一个节点指向头指针if curBoy.Next == first {break}curBoy = curBoy.Next}return count}// 解决约瑟夫问题的算法
// k : 约定编号为 k 的人从 1 开始报数
// m : 从1开始报数,数到 m 的人出列
func Josephu(first *Boy, k int, m int) {fmt.Println("\n小孩出圈的顺序如下:")// 单独处理空链表if first.Next == nil {fmt.Println("empty link list")return}if k > CountBoy(first) {fmt.Printf("输入的参数 %d 有误\n", k)return}// 定义一个辅助指针,帮助删除小孩tail := first// 让 tail 指向环形链表的最后一个节点(小孩)// 后面 tail 和 first 都往后挪动的时候// 才能保证 tail 一直在 fist 后面for {// 说明已经指向了最后一个节点// 退出循环if tail.Next == first {break}tail = tail.Next}// 让 first 移动到 k (编号为 k 的人开始报数)// 移动到编号 k ,只需要移动 k - 1 次for i := 0; i < k-1; i++ {first = first.Nexttail = tail.Next}// 开始数 m 下(数到 m 的人出列)// 然后就删除 first 指向的小孩for {for i := 0; i < m-1; i++ {first = first.Nexttail = tail.Next}fmt.Printf("编号为 %d 的小孩出圈\n", first.No)// 删除 first 指向的小孩first = first.Nexttail.Next = first// 如果圈中只有一个小孩if first == tail {fmt.Printf("编号为 %d 的小孩出圈\n", first.No)break}}}func main() {first := AddBoy(5)ShowBoy(first)Josephu(first, 2, 3)
}
执行结果
小孩编号 = 1 -> 小孩编号 = 2 -> 小孩编号 = 3 -> 小孩编号 = 4 -> 小孩编号 = 5 ->
小孩出圈的顺序如下:
编号为 4 的小孩出圈
编号为 2 的小孩出圈
编号为 1 的小孩出圈
编号为 3 的小孩出圈
编号为 5 的小孩出圈
约瑟夫问题(Josephus problem)详解相关推荐
- HDU-2993--MAX Average Problem详解
此题是关于DP的优化问题,具体解题思路贴在后面 此题大意: 读入一列正数N,a1, a2, -, aN,以及一个数F.定义ave(i,j)=ai到aj的平均值,j-i+1>=k, 求一个最大的a ...
- 约瑟夫问题(Josephus Problem)的两种快速递归算法
参考:http://haoyuanliu.github.io/2016/04/18/Josephus/ 转载于:https://www.cnblogs.com/xiaoshayu520ly/p/102 ...
- halting problem 详解
在讲解halting problem 问题之前,我们先来看一张图和一个小故事 图片: 图片来自网络,如有侵权请联系删除 故事: 理发师悖论: 在一个村子里只有有一个理发师,他说他只给不给自己理发的 ...
- 约瑟夫环问题的两种解法(详解)
约瑟夫环问题的两种解法(详解) 题目: Josephus有过的故事:39 个犹太人与Josephus及他的朋友躲到一个洞中,39个犹太人决定宁愿死也不要被敌人抓.于是决定了自杀方式,41个人排成一个圆 ...
- Joseph Problem(解约瑟夫问题)
今天在一个OJ上做了一个Joseph Problem(解约瑟夫问题)的题,题目不难,直接用循环链表模拟实际操作即可完成,但是用此种方法的时间太长,超时,所以我就用了一个大家对这类问题比较常用的解法-- ...
- C语言解决约瑟夫问题详解的代码
C语言解决约瑟夫问题详解的代码 参考文章: (1)C语言解决约瑟夫问题详解的代码 (2)https://www.cnblogs.com/odsxe/p/10791049.html (3)https:/ ...
- 约瑟夫环——递推公式详解(leetcode 1823. 找出游戏的获胜者)
约瑟夫环--递推公式详解(leetcode 1823. 找出游戏的获胜者) 约瑟夫环问题 约瑟夫环(约瑟夫问题)是一个数学的应用问题:已知 n 个人(以编号1,2,3-n分别表示)围坐在一张圆桌周围. ...
- Josephus Problem的详细算法及其Python, Java语言的实现
笔者昨天看电视,偶尔看到一集讲述古罗马人与犹太人的战争--马萨达战争,深为震撼,有兴趣的同学可以移步:http://finance.ifeng.com/a/20170627/15491157_0. ...
- 位运算详解+竞赛常见用法总结
目录 一.位运算详解 二.位运算应用 1.快速幂 2.给定一个数组A, 长度为n,求下面这段程序的值 3.数数字 4.数数字 2 5.nim博弈问题: 6.树状数组 7.判断一个数x是不是2的某次方 ...
- Java源码详解四:String源码分析--openjdk java 11源码
文章目录 注释 类的继承 数据的存储 构造函数 charAt函数 equals函数 hashCode函数 indexOf函数 intern函数 本系列是Java详解,专栏地址:Java源码分析 Str ...
最新文章
- 工信部通告:任何组织和机构不得继续实施“计算机信息系统集成企业资质认定”...
- Cobbler无人值守安装系统史上最细实践文档
- 【原】webpack--plugins,主要解释plugins干了啥
- Servlet读取xml文件的配置参数
- Sklearn fit , transform ,fit_transform
- 数值分析(11)-数值积分
- 计算机能否代替老师英语作文,雅思大作文范文:电脑不可取代老师
- angular directive详解
- Iterative混沌映射
- PHP 图片木马隐写方法及靶机演示
- 服务更新发布方式------“金丝雀、滚动更新、蓝绿部署”
- linux被病毒感染 CPU使用很高
- 马斯克坚信的“矩阵模拟”,是一种怎样“烧脑”的存在?
- rand函数用法整理
- 5000万存银行,一年利息够不够花呢?
- 利用加速度求解位置的算法——三轴传感器
- CentOS 7之ifconfig
- Linux之gstreamer视频编解码测试指令
- VS 2015 社区版(Community) 试用期(30天)之后,账户登录
- [书]x86汇编语言:从实模式到保护模式 -- 第16章 分页机制、平坦模型
热门文章
- linux查看各端口号,端口如何查看linux的端口号
- 调音台docker教程_超详细Docker实战教程,万字详解!
- 计算机毕业设计ssm毕业论文管理系统b909r系统+程序+源码+lw+远程部署
- 程序员进阶必备--写文档
- 15-Mixly模拟输出 | Mixly技巧系列
- 电脑右键新建没有文本文档解决办法
- 人脸识别系统_设计说明书
- meanshift算法通俗讲解
- PackageManager(管理应用程序包)解析
- request.getParameterValues()用法