算法科普:什么是约瑟夫环
1 问题描述
约瑟夫环(约瑟夫问题)是一个数学的应用问题:已知 n 个人(以编号1,2,3…n分别表示)围坐在一张圆桌周围。从编号为 k 的人开始报数,数到 m 的那个人出圈;他的下一个人又从 1 开始报数,数到 m 的那个人又出圈;依此规律重复下去,直到剩余最后一个胜利者。
例如:有10个人围成一圈进行此游戏,每个人编号为 1-10 。若规定数到 3 的人出圈。则游戏过程如下。
(1)开始报数,第一个数到 3 的人为 3 号,3 号出圈。
1, 2, 【3】, 4, 5, 6, 7, 8, 9, 10。
(2)从4号重新从1开始计数,则接下来数到3的人为6号,6号出圈。
1, 2, 【3】, 4, 5, 【6】, 7, 8, 9, 10。
(3)从7号重新从1开始计数,则接下来数到3的人为9号,9号出圈。
1, 2, 【3】, 4, 5, 【6】, 7, 8, 【9】, 10。
(4)从10号重新从1开始计数,由于10个人称环形结构,则接下来数到3的人为2号,2号出圈。
1, 【2】, 【3】, 4, 5, 【6】, 7, 8, 【9】, 10。
(5)从4号重新从1开始计数,则接下来数到3的人为7号,7号出圈。
1, 【2】, 【3】, 4, 5, 【6】, 【7】, 8, 【9】, 10。
(6)从8号重新从1开始计数,则接下来数到3的人为1号,1号出圈。
【1】, 【2】, 【3】, 4, 5, 【6】, 【7】, 8, 【9】, 10。
(7)从4号重新从1开始计数,则接下来数到3的人为8号,8号出圈。
【1】, 【2】, 【3】, 4, 5, 【6】, 【7】, 【8】, 【9】, 10。
(8)从10号重新从1开始计数,则接下来数到3的人为5号,5号出圈。
【1】, 【2】, 【3】, 4, 【5】, 【6】, 【7】, 【8】, 【9】, 10。
(9)从10号重新从1开始计数,则接下来数到3的人为10号,10号出圈。
【1】, 【2】, 【3】, 4, 【5】, 【6】, 【7】, 【8】, 【9】, 【10】。
(10)最终剩余 4 号,4 号为胜利者。
2 数组求解
2.1 解题思想
用数组求解的基本思想就是用一个一维数组去标识这 n 个人的状态,默认全为 1 ,也就是都在圈子内,当数到 m的人出圈之后,标识置为 0(就是出圈了),同时报数器清 0,下一个人要从 1 开始。在每次报数之前要判断他是否在圈子内(也就是他的标识是否为 1 ),如果在圈子里面才会继续报数。定义一个变量记录出圈的人数, 出圈的人数等于 n-1 时,则游戏结束。
2.2 代码实现
#include<stdio.h>#define N 1000000 //记录玩游戏最大人数int flag【N】 = {0};int main(){ int n = 0, m = 0; scanf("%d%d", &n, &m);//输入玩游戏人数和计数m int i = 0; int count = 0; //记录已经出圈的人数 int num = 0; //报数器 for(i = 1; i <= n; i++) { flag【i】 = 1;//所有人入圈 } while(count < n - 1) { for(i = 1; i <= n; i++ ) { if (1 == flag【i】) {//在未出圈的人数中计数 num++; if(num == m) {//此人数到m则出圈 printf("%d\n", i); count++;//出圈人数加1 flag【i】 = 0;//出圈 num = 0; } if(count == n - 1) { break; } } } } for(i = 1; i <= n; i++) { if(1 == flag【i】) {//输出赢家,标识为1 printf("The last one is : %d\n", i); } } return 0;}
3 循环链表求解
3.1 解题思想
约瑟夫环问题可以转化为循环链表的数据结构来求解。可以将每个人看做链表的单个节点,每个节点之间通过链表的 next 指针连接起来,并且将链表末尾节点指向头节点就形成的环,由链表构成的环形结构在数据结构中称为循环链表。
3.2 代码实现
#include <stdio.h>#include <stdlib.h>
/*声明一个链表节点*/typedef struct node { int number;//数据域,存储编号数值 struct node *next;//指针域,指向下一个节点}Node;
/*创建链表节点的函数*/ Node* CreatNode(int x) { Node *p; p = (Node*)malloc(sizeof(Node)); p->number = x;//将链表节点的数据域赋值为编号 p->next = NULL; return p;}
/*创建环形链表,存放整数1到n*/Node* CreatJoseph(int n) { Node *head,*p,*q; int i; for(i = 1; i <= n; i++) { p = CreatNode(i);//创建链表节点,并完成赋值 if(i == 1)//如果是头结点 head = p; else//不是头结点,则指向下一个节点 q->next = p; q = p; } q->next = head;//末尾节点指向头结点,构成循环链表 return head;}
/*模拟运行约瑟夫环,每数到一个数,将它从环形链表中删除,并打印出来*/void RunJoseph(int n,int m) { Node *p,*q; p = CreatJoseph(n);//创建循环链表形式的约瑟夫环 int i; while(p->next != p)//循环条件,当前链表数目大于1 { for(i = 1; i < m-1; i++)//开始计数 { p = p->next; } //第m个人出圈 q = p->next; p->next = q->next; p = p->next; printf("%d--",q->number);//输出出圈的序号 free(q); } printf("\n最后剩下的数为:%d\n",p->number);}
int main(){ int n,m; scanf("%d %d",&n,&m); RunJoseph(n,m); return 0;}
4 递推公式求解
4.1 解题思想
约瑟夫环中,每当有一个人出圈,出圈的人的下一个人成为新的环的头,相当于把数组向前移动 m 位。若已知 n-1 个人时,胜利者的下标位置位 f(n−1,m) ,则 n 个人的时候,就是往后移动 m 位,(因为有可能数组越界,超过的部分会被接到头上,所以还要模 n ),根据此推导过程得到的计算公式为:
f(n,m) = (f(n−1,m) + m) % n。
其中,f(n,m) 表示 n 个人进行报数时,每报到 m 时杀掉那个人,最终的编号,f(n−1,m) 表示,n-1 个人报数,每报到 m 时杀掉那个人,最终胜利者的编号。有了递推公式后即可使用递归的方式实现。
4.2 递归代码实现
#include <stdio.h>int Joseph(int n,int m)/*计算约瑟夫环的递归函数*/{ if(n <= 1 || m <= 1)//设置游戏人数限定值 return -1;
if(n == 2)//设置边界值 { if(m % 2 == 0) return 1; else return 2; } else { return (Joseph(n-1,m) + m-1) % n+1;//递归调用 }}
int main(){ int n,m,x; scanf("%d %d",&n,&m); x=Joseph(n,m); printf("最后一个数为:%d\n",x); return 0;}
4.3 迭代代码实现
#include <stdio.h>/*计算约瑟夫环问题的迭代法函数*/int Joseph(int n,int m){ int i; int x,y; if(n <= 1 || m <= 1) return -1; if(m % 2 == 0) y = 1; else y = 2;
for(i = 3; i <= n; i++) { x = (y-1 + m) % i + 1; y = x; } return y;}
int main(){ int n,m,x; scanf("%d %d",&n,&m); x = Joseph(n,m); printf("最后一个的编号是:%d\n",x); return 0;}
实际应用
比如你们公司需要团建,你可以设计这个游戏,根据游戏人数的多少,将你和你心仪的妹子安排在合适的座位上,一同进最后的决赛圈~
推荐阅读
拜托,面试官别问我「布隆」了
数据结构与算法:三十张图弄懂「图的两种遍历方式」
昨天,终于拿到了腾讯 offer
几道和「二叉树」有关的算法面试题
几道和散列(哈希)表有关的面试题
一道看完答案你会觉得很沙雕的「动态规划算法题」
几道和「堆栈、队列」有关的面试算法题
链表算法面试问题?看我就够了!
欢迎关注
算法科普:什么是约瑟夫环相关推荐
- 程序员面试系列——约瑟夫环
约瑟夫斯问题(Josephus Problem) 约瑟夫斯问题(有时也称为约瑟夫斯置换),是一个出现在计算机科学和数学中的问题.在计算机编程的算法中,类似问题又称为"约瑟夫环",也 ...
- 约瑟夫环数据结构c语言程序,数据结构的C语言(类C语言)--单向循环链表--约瑟夫环...
代码区 约瑟夫环:用类C语言实现!!!可以成功运行!!!不是仅仅的算法,而是实实在在的类C #include #include typedef int ElemType; typedef struct ...
- c/c++常用算法(14) -- 经典数据结构(约瑟环夫问题)
一.简单的约瑟环夫 1.故事描述: 据说著名犹太历史学家 Josephus有过以下的故事:在罗马人占领乔塔帕特后,39 个犹太人与Josephus及他的朋友躲到一个洞中,39个犹太人决定宁愿死也不要被 ...
- 约瑟夫环Java实现
面试中可能经常会遇到约瑟夫环问题,逻辑上很简单,就是看怎么实现了,一般而言,最简单最直观的就是利用链表,然后构建一个循环结构,正好是环,最后计算出结果. 遍历环形链表会是一个无限循环,如果链表中的数据 ...
- java 实现约瑟夫环
这一次是借鉴模仿别人写的代码,以前觉得不好将数据结构的链结构什么的迁移到java上来使用,但这一次确实让我感受到了可以自己构造数据结构,然后使用类似链的方式来解决约瑟夫环,有所顿悟.不多说,继续上代码 ...
- # R语言——约瑟夫环
约瑟夫环: n个人围成一个圈,从第一个人点名,每数到第三个人,这个人移出圈外, 依次类推,求最后留下来的人编号是? 思路:每次循环重新编码序号作为names,并根据names 进行筛选 拓展:约瑟夫环 ...
- 循环列表实现约瑟夫环
1.作业需求 利用循环列表实现约瑟夫环 looplink.h来实现函数声明 #ifndef __LOOPLINK_H__ #define __LOOPLINK_H__ typedef int data ...
- 浙江高考VB之约瑟夫环
浙江信息技术Giao考之 "约瑟夫环" 在浙江信息技术高考中,有一种题型叫做 约瑟夫环题; 首先,约瑟夫环是什么东西? 鲜活的栗子: 我们现在有6个小朋友,分别标号为1 ~ 6.从 ...
- n个人围成一个圈报3,或者约瑟夫环,或者丢手绢
标题:n个人围成一个圈报3,或者约瑟夫环,或者丢手绢 下面这个视频,可以让你了解什么是约瑟夫环,此后,自己写代码实现一下,可以看看我的代码作为参考, 参考视频: 调用,n=10,编号从1-10,sta ...
最新文章
- Misc-wireshark-1(秒懂!!)
- 'vue' 不是内部或外部命令
- java 虚拟机 新生代与老年代gc_java 虚拟机--新生代与老年代GC
- Android之最好理解的Binder机制
- 单片机计算机课程设计报告,单片机课程设计报告(简易计算机).doc
- IMDB Top500(世界最佳电影500部)
- Linux mmc驱动框架(4)——卡检测及初始化
- OpenCV人脸识别的原理 (原文完整版)
- 抢不到回家的票,还真不是12306技术不行
- 高德地图使用vue-amap 自定义点坐标
- Php 领域驱动 视频,.Net DDD 领域驱动学习视频教程
- VMware 安装ghost win7 gho
- Unity URP入门实战
- 重识Nginx - 15 使用信号管理Nginx的父子进程
- Windows 7怎么让电脑定时关机?Windows 7怎么取消自动关机?
- 【JS迷你书】Number类型二进制表示法
- 单元测试:通过读取csv/xml数据并且结合使用allure展示测试报告,验证开发中的add()和reduct()操作(在@allure.story分别实现相加减)
- xml基础、DTD验证、Schema验证(备忘)
- Win7 10安装Office2010提示让安装MSXML组件的五种解决方法
- Codeforces Round #826 (Div. 3)(A~F)
热门文章
- SSMS Tools Pack 4.9.6破解补丁
- Minio(二) | Minio多用户权限控制
- onclick 常用手册
- 什么样的无线蓝牙耳机好?综合性能最好的蓝牙耳机
- 怎么从S60 Epoc上删除应用程序
- R语言使用rev函数对日期向量数据进行反序处理(逆序处理、reverse)
- 人事管理java 课程设计_数据库+Java课程设计 人事管理系统 (一)
- 菜鸟级测试开发者日记1 2020总结碎碎念看破红尘般的展望
- html实现滑动解锁_javascript实现滑动解锁功能
- 微信小程序如何修改第三方组件样式 例如 vant-weapp样式修改