题目:约瑟夫环问题

合集下载

实验一约瑟夫环问题实验报告

实验一约瑟夫环问题实验报告

题目二约瑟夫环问题设编号为1,2,3,……,n 的n(n>0)个人按顺时针方向围坐一圈,每个人持有一个正整数密码。

开始时任选一个正整数做为报数上限m ,从第一个人开始顺时针方向自1起顺序报数,起顺序报数,报到报到m 时停止报数,时停止报数,报报m 的人出列,的人出列,将他的密码作为将他的密码作为新的m 值,从他的下一个人开始重新从1报数。

报数。

如此下去,如此下去,如此下去,直到所有人全部出列直到所有人全部出列为止。

令n 最大值取30。

要求设计一个程序模拟此过程,求出出列编号序列。

struct node //结点结构{ int number; /* 人的序号人的序号*/ int cipher; /* 密码密码*/ struct node *next; /* 指向下一个节点的指针*/ }; 一、循环链表的结点类型定义/* 单链表的结点类型 */typedefstruct node{int number;int cipher;struct node *next;}list, *linklist;二、循环链表的初始化/* 函数功能:初始化n 个元素的循环链表参数;链表(linklist L),元素个数(int n )通过后插法对无头结点的链表初始化。

*/voidinit(linklist&L,int n){int key, i;cout<<"输入第1个人的密码为:";//输入第一个节点的密码。

cin>>key;L= new list;L->number = 1;L->cipher = key;L->next = L;for(i = 2; i<= n; i ++)//输入2—n 的节点密码。

{linklist p = new list;cout<<"输入第"<<i<<"个人的密码为:";cin>>key;p->cipher = key;p->number = i;p->next = L->next; //使用后插法插入。

约瑟夫环问题(Josephus)

约瑟夫环问题(Josephus)

算法设计
Josephus jp=new Josephus(); int a[]=new int[n]; for(int i=0;i<n;i++){ a[i]=i+1; } jp.SortArray(a,n,m,k,g); } public void show(int[]b,int g){ for(int i=b.length-g;i<b.length;i++){ System.out.print(b[i]+" "); } }
• b[c]=a[i]; • a[i]=0; • c++; • if(c==n) break; • } • System.out.print(“最后出列的 3人: "); • this.show(b,g); • } • }
• 1.数据选择: 要求:n<2^15; 1<=k<=n; 2.数据和结果显示:
(3)当然其中还是会存在一些漏洞,需要进 一步的改进。在计算机中是容不得丝毫的 错误的,这也让我们学到了面对科学要持 有严谨的态度,否则必定得不到应该有的 结果。
总人数n 起始号码k 循环数m 最后出列的3人 总人数n 起始号码k 循环数m 最后出列的3人 总人数n 起始号码k 循环数m 最后出列的3人 48 6 15 47 21 46 105 73 4 87 32 21 300 80 12 70 296 198 总人数n 起始号码k 循环数m 68 34 25
输出格式:
T行最后min(n,3)个出列的编号。 结果:6 1 5
问题背景
• 这个问题是以弗拉维奥•约瑟夫斯命名的, 它是1世纪的一名犹太历史学家。他在自己 的日记中写道,他和他的40个战友被罗马 军队包围在洞中。他们讨论是自杀还是被 俘,最终决定自杀,并以抽签的方式决定 谁杀掉谁。约瑟夫斯和另外一个人是最后 两个留下的人。约瑟夫斯说服了那个人, 他们将向罗马军队投降,不再自杀。

约瑟夫环问题实验报告

约瑟夫环问题实验报告

约瑟夫问题实验报告背景约瑟夫问题(Josephus Problem)据说著名犹太历史学家Josephus有过以下的故事:在罗马人占领乔塔帕特后,39 个犹太人与Josephus及他的朋友躲到一个洞中,39个犹太人决定宁愿死也不要被敌人到,于是决定了一个自杀方式,41个人排成一个圆圈,由第1个人开始报数,每报数到第3人该人就必须自杀,然后再由下一个重新报数,直到所有人都自杀身亡为止。

然而Josephus 和他的朋友并不想遵从,Josephus要他的朋友先假装遵从,他将朋友与自己安排在第16个与第31个位置,于是逃过了这场死亡游戏。

原题:用户输入M,N值,N个人围成一个环,从0号人开始数,数到M,那个人就退出游戏,直到最后一个人求最后一个剩下的人是几号?问题描述设编号为1-n的n(n>0)个人按顺时针方向围成一圈.首先第1个人从1开始顺时针报数.报m的人(m 为正整数).令其出列。

然后再从他的下一个人开始,重新从1顺时针报数,报m的人,再令其出列。

如此下去,直到圈中所有人出列为止。

求出列编号序列。

一.需求分析:(1)基本要求需要基于线性表的基本操作来实现约瑟夫问题需要利用循环链表来实现线性表(2)输入输出格式输入格式:n,m(n,m均为正整数,)输出格式1:在字符界面上输出这n个数的输出序列(3)测试用例(举例)输入:8,4输出:4 8 5 2 1 3 7 6二.概要设计(1)抽象数据类型:数据对象:n个整数数据关系:除第一个和最后一个n外,其余每个整数都有两个元素与该元素相邻。

基本操作:查找,初始化,删除,创建链表循环链表的存储结构:(2).算法的基本思想循环链表基本思想:先把n个整数存入循环链表中,设置第m个数出列,从第一个开始查找,找到第m个时,输出第m个数,并删掉第m个节点,再从下一个数开始查找,重复上一步骤,直到链表为空,结束。

(3).程序的流程程序由三个模块组成:1.输入模块:完成两个正整数的输入,存入变量n和m中2.处理模块:找到第m个数3.输出模块:按找到的顺序把n个数输出到屏幕上三.详细设计首先,设计实现约瑟夫环问题的存储结构。

约瑟夫环-循环报数问题

约瑟夫环-循环报数问题

约瑟夫环-循环报数问题
约瑟夫游戏的⼤意:30个游客同乘⼀条船,因为严重超载,加上风浪⼤作,危险万分。

因此船长告诉乘客,只有将全船⼀半的旅客投⼊海中,其余⼈才能幸免于难。

⽆奈,⼤家只得同意这种办法,并议定30 个⼈围成⼀圈,由第⼀个⼈数起,依次报数,数到第9⼈,便把他投⼊⼤海中,然后再从他的下⼀个⼈数起,数到第9⼈,再将他投⼊⼤海中,如此循环地进⾏,直到剩下 15 个游客为⽌。

问:哪些位置是将被扔下⼤海的位置?
#include<bits/stdc++.h>
using namespace std;
int main()
{
int n, m,i,s=0;
cin>>n; //总⼈数
cin>>m; //m个⼀个报数循环(注意参数下标问题)
for(i=2;i<=n;i++)
s=(s+m)%i;
printf("%d", s+1);
return 0;
}。

约瑟夫环

约瑟夫环

k:计数 m:密码
4
6 9
4 22
出队序列: 出队序列: 5
7
约瑟夫环(Joseph Circle) 4
例:
start 1 3 7 15 k=4 2 8 3 1
k:计数 m:密码
6 9
4 22
出队序列: 出队序列: 5
8
约瑟夫环(Joseph Circle)
例:
start 1 3 7 15
k:计数 m:密码
1
约瑟夫环(Joseph Circle)
例:
start 5 k=1 1 3 2 8 3 1
k:计数 m:密码
7 15
6 9
4 22 5 4
出队序列: 出队序列:
2
约瑟夫环(Joseph Circle) 5
例:
start 1 3 7 15 k=2 2 8 3 1
k:计数 m:密码
6 9
4 22 5 4
出队序列: 出队序列:
3
约瑟夫环(Joseph Circle)
例:
start 1 3 7 15 2 8
k:计数 m:密码
3 k=3 5 1
6 94 22 5 4出队序列: 出队序列:
4
约瑟夫环(Joseph Circle)
例:
start 1 3 7 15 2 8 3 1 4 k=4 5 22 5 4
3 k=1 8 1
6 9
4 22
出队序列: 出队序列: 5
2
9
约瑟夫环(Joseph Circle)
例:
start 1 3 7 15 k=8 3 1
k:计数 m:密码
8
6 9
4 22

约瑟夫环问题

约瑟夫环问题

约瑟夫环问题问题描述:有n个⼈,编号分别从0到n-1排列,这n个⼈围成⼀圈,现在从编号为0的⼈开始报数,当报到数字m的⼈,离开圈⼦,然后接着下⼀个⼈从0开始报数,依次类推,问最后只剩下⼀个⼈时,编号是多少?分析:这就是著名的约瑟夫环问题,关于来历不再说明,这⾥直接分析解法。

解法⼀:蛮⼒法。

我曾将在⼤⼀学c语⾔的时候,⽤蛮⼒法实现过,就是采⽤标记变量的⽅法即可。

解法⼀:循环链表法。

从问题的本质⼊⼿,既然是围成⼀个圈,并且要删除节点,显然符合循环链表的数据结构,因此可以采⽤循环链表实现。

解法三:递推法。

这是⼀种创新的解法,采⽤数学建模的⽅法去做。

具体如下:⾸先定义⼀个关于n和m的⽅程f(n,m),表⽰每次在n个编号0,1,...,n-1中每次删除的报数为m后剩下的数字,在这n个数字中,第⼀个被删除的数字是(m-1)%n,为了简单,把(m-1)%n记作k,那么删除k之后剩下的数字为0,1,2,...,k-1,k+1,...,n-1并且下⼀次删除的数字从k+1开始计数,这就相当于剩下的序列中k+1排在最前⾯,进⽽形成k+1,..,n-1,0,1,2,...,k-1这样的序列,这个序列最后剩下的数字应该和原序列相同,由于我们改变了次序,不能简单的记作f(n-1,m),我们可以记作g(n-1,m),那么就会有f(n,m)=g(n-1,m).下⼀步,我们把这n-2个数字的序列k+1,..,n-1,0,1,2,...,k-1做⼀个映射,映射的结果是形成⼀个从0到n-2的序列。

k+1对0,k+2对1,......,n-1对n-k-2,0对n-k-1,1对n-k,....,k-1对n-2这样我们可以把这个映射定义为p,则p(x)=(x-k-1)%n,它表⽰如果映射前的数字是x,映射后为(x-k-1)%n,从⽽这个映射的反映射问为p-1(x)=(x+k+1)%n由于映射之后的序列和原始序列具有相同的形式,都是从0开始的序列,所以可以⽤函数f来表⽰,即为f(n-1,m),根据映射规则有:g(n-1,m)=p-1[f(n-n,m)]=[f(n-1,m)+k+1]%n,最后把之前的k=(m-1)%n带⼊式⼦就会有f(n,m)=g(n-1,m)=[f(n-1,m)+m]%n.这样我们就可以得出⼀个递推公式,当n=1时,f(n,m)=0;当n>1时,f(n,m)=[f(n-1,m)+m]%n;有了这个公式,问题就变得多了。

趣学算法——约瑟夫环问题(java版)

趣学算法——约瑟夫环问题(java版)

趣学算法——约瑟夫环问题(java版)1 什么是约瑟夫环问题?约瑟夫,是⼀个古犹太⼈,曾经在⼀次罗马叛乱中担任将军,后来战败,他和朋友及另外39个⼈躲在⼀⼝井⾥,但还是被发现了。

罗马⼈表⽰只要投降就不死,约瑟夫想投降,可是其他⼈坚决不同意。

怎么办呢,他想到⼀个主意:让41个⼈围成⼀个圆圈,从第⼀个⼈开始报数,数到3的那个⼈被旁边的⼈杀死。

这样就可以避免⾃杀了,因为犹太⼈的信仰是禁⽌⾃杀的。

结果⼀群⼈杀来杀去最后只剩下两个了,就是约瑟夫和他朋友,于是两⼈愉快地去投降了。

约瑟夫和朋友站在什么位置才保住了性命呢,这就是我们今天要讲的约瑟夫环问题。

2 问题的重要性这是个BAT常⽤⾯试题,⽽且本质上是⼀个游戏,可以⼴泛应⽤于⽣活中,⼯作⽣活好帮⼿就是它了。

3 约瑟夫环抽象问题这个问题实际在讲:N个⼈围成⼀圈,第⼀个⼈从1开始报数,报M的被杀掉,下⼀个⼈接着从1开始报,循环反复,直到剩下最后⼀个,那最后胜利者的初始位置在哪⾥?模拟流程:假如有5个⼈报数,报到3被杀,情况如下A B C1 D E (初始位置,C第⼀个被杀)D E A2 B (C死后的第⼆次排位,A第⼆个被杀)B D E3 (A死后的第三次排位,E第三个被杀)B4 D (E死后的第四次排位,B第四个被杀)D (D留在了最后,初始位置是4)解决⽅法:1 循环遍历法public static int josephus(int n, int m) {//n个⼈, 0 1 2..n-1int[] people = new int[n];//⼈的索引int index = -1;//报数记录, 1 2 3..mint count = 0;//剩余⼈数初始值为nint remain = n;//为了找到最后⼀个幸存者的位置,假设所有⼈都会被杀while (remain > 0) {index++; //找到报数的⼈if (index == n) { //所有⼈遍历⼀圈后从头遍历index = 0;}if (people[index] == -1) { //如果当前的⼈被杀跳过continue;}count++; //报数if (count == m) {people[index] = -1; //报数到m后杀⼈count = 0; //报数重置remain--; //剩余⼈数递减}}return index;}将41传⼊⽅法后,可得结果为30, 因为是从0开始计数,所以等价于现实世界的第31位。

数据结构约瑟夫环问题

数据结构约瑟夫环问题

数据结构实验报告题目:约瑟夫环问题一.设计内容[问题描述]约瑟夫环问题的一种描述是:编号为1, 2, 3,…,n的n个人按顺时针方向围坐一圈,每人手持一个密码(正整数)。

一开始任选一个整数作为报数上限值,从第一人开始顺时针自 1 开始顺序报数,报到m 时停止报数。

报m 的人出列, 将它的密码作为新的m 值,从他在顺时针方向上的下一个人开始重新从 1 报数, 如此下去直到所有人全部出列为止。

试设计程序实现之。

[基本要求] 利用循环链表存储结构模拟此过程,按照出列的顺序打印各人的编号。

[ 实验提示] 程序运行后首先要求用户指定初始报数上限值。

然后读取各人的密码。

设n<=30 。

程序执行后,要求用户在计算机终端上显示“提示信息”后,用键盘输入“提示信息”中规定的命令,以“回车符”为结束标志。

相应的输入数据和运算结果显示在其后。

二、设计目的1. 达到熟练掌握C++ 语言的基本知识和技能;2. 能够利用所学的基本知识和技能,解决简单的面向对象程序设计问题。

3. 把课本上的知识应用到实际生活中,达到学以致用的目的。

三、系统分析与设计(确定程序功能模块)1、为实现上述程序的功能,应以有序链表表示集合。

基本操作:InitList(&L)操作结果:构造一个空的有序表L。

DestroyList(&L)初始条件:有序表L 已存在。

操作结果:销毁有序表L。

ListEmpty(L)初始条件:有序表L 已存在。

操作结果:若L为空表,则返回TRUE,否则返回FALSE。

ListLength(L)初始条件:有序表L 已存在。

操作结果:返回L 中数据元素个数。

GetElem(L,i)初始条件:有序表L已存在,并且K i< ListLength(L)。

操作结果:返回L 中第i 个数据元素。

LocatePos(L,e)初始条件:有序表L已存在,e和有序表中元素同类型的值。

操作结果:若L中存在和e相同的元素,则返回位置;否则返回0。

约瑟夫环问题的两种解法(详解)

约瑟夫环问题的两种解法(详解)

约瑟夫环问题的两种解法(详解)约瑟夫环问题的两种解法(详解)题⽬:Josephus有过的故事:39 个犹太⼈与Josephus及他的朋友躲到⼀个洞中,39个犹太⼈决定宁愿死也不要被敌⼈抓。

于是决定了⾃杀⽅式,41个⼈排成⼀个圆圈,由第1个⼈开始报数,每报数到第3⼈该⼈就必须⾃杀。

然后下⼀个重新报数,直到所有⼈都⾃杀⾝亡为⽌。

然⽽Josephus 和他的朋友并不想遵从,Josephus要他的朋友先假装遵从,他将朋友与⾃⼰安排在第16个与第31个位置,于是逃过了这场死亡游戏。

对于这个题⽬⼤概两种解法:⼀、使⽤循环链表模拟全过程⼆、公式法我们假设这41个⼈编号是从0开始,从1开始报数,第3个⼈⾃杀。

1、最开始我们有这么多⼈:[ 0 1 2 3 4 5 ... 37 38 39 40 ]2、第⼀次⾃杀,则是(3-1)%41=2 这个⼈⾃杀,则剩下:[ 0 1 3 4 5 ... 37 38 39 40 ]3、然后就是从编号为3%41=3的⼈开始从1报数,那么3号就相当于头,既然是头为什么不把它置为0,这样从它开始就⼜是与第1,2步⼀样的步骤了,只是⼈数少了⼀个,这样不就是递归了就可以得到递归公式。

想法有了就开始做:4、把第2步中剩下的⼈编号减去3映射为:[ -3 -2 0 1 2 ... 34 35 36 37 ]5、出现负数了,这样不利于我们计算,既然是环形,37后⾯报数的应该是-3,-2,那么把他们加上⼀个总数(相当于加上360度,得到的还是它)[ 38 39 0 1 2 3 ... 34 35 36 37 ]6、这样就是⼀个总数为40个⼈,报数到3杀⼀个⼈的游戏。

这次⾃杀的是第5步中的(3-1)%40=2号,但是我们想要的是第2步中的编号(也就是最初的编号)那最初的是多少?对应回去是5;这个5是如何得到的呢?是(2+3)%41得到的。

⼤家可以把第5步中所有元素对应到第2步都是正确的。

7、接下来是[ 35 36 37 38 0 1 2... 31 32 33 34 ]⾃杀的是(3-1)%39=2,先对应到第5步中是(2+3)%40=5,对应到第2步是(5+3)%41=8。

约瑟夫环问题的三种解法

约瑟夫环问题的三种解法

约瑟夫环问题的三种解法约瑟夫问题是个著名的问题:N个⼈围成⼀圈,第⼀个⼈从1开始报数,报到k的⼈将被杀掉,接着下⼀个⼈⼜从1开始报,直到最后剩下⼀个,求最后留下的⼈的下标。

题⽬集合解法1:暴⼒可以直接暴⼒求解,时间复杂度为O(nk)解法2:递推设f(n,k)为当n个⼈围成⼀圈时,最后留下的⼈的下标。

对于f(n-1,k)来说,其结果相当于f(n,k)的结果向前移动k\%(n-1)位。

因为对于f(n,k)来说,去掉第⼀轮报的数(k\%n)后,现在就只剩下n-1个数,并且是以(k\%(n-1)+1)作为第⼀个数,即所有数向前移动k\%(n-1)位。

现在的结果就为f(n-1,k)对于f(5,3)来说,其结果为4。

当其去掉第⼀轮报的数后,其向前移动了(3\%4)位,以4为起始,f(4,3)结果为1,对应着f(5,3)的结果4向前移动了3位所以反过来看即为,即为f(n-1,k)的结果向后移动k\%(n-1)位即f(n+1,k)=(f(n,k)+k\%n)\%n (x下标从0开始,因为取模结果为[0,n-1])时间复杂度为O(n)ll josephus2(ll n,ll k){ll pos=0;for(int len=1;len<=n;len++){pos = (pos+k)%len;}return pos+1;}递推代码解法3:如果当前这⼀位⼈没被杀掉,则他可以放在幸存者的末尾,直到幸存者数量为1所以对于下标为i的⼈,如果在他前⾯已经被杀掉了q个⼈,那么他的新的下标为n+q(k-1)+x,(1\leq x <k)如下图所⽰,最后被淘汰的编号⼀定是n*k,所以幸存者最后的编号是n*k我们现在需要从幸存者最后的编号中恢复出最初编号假设幸存者这⼀次的编号为p os_{i},在他后⾯包括他还有x位幸存者,则[pos_{i-1},pos_{i})间⼀定有x个不能被k整除的数这样才能使在他后⾯包括他还有x位幸存者。

约瑟夫环问题知识点总结

约瑟夫环问题知识点总结

约瑟夫环问题知识点总结约瑟夫环问题的描述如下:有n个人(编号从1到n),他们围成一个环形。

从第一个人开始报数,数到m的人被杀掉,然后从被杀掉的人的下一个人开始重新报数,直到所有人都被杀掉为止。

问题的解是最后剩下的那个人的编号。

约瑟夫环问题在计算机科学和数学领域都有着广泛的应用,因为它涉及到循环队列、递归、数学归纳法等多个概念。

以下是约瑟夫环问题的一些重要知识点总结:1. 约瑟夫环问题的递归解法递归是解决约瑟夫环问题的一种常见的方法。

基本思路是将问题分解为规模更小的子问题,并通过解决子问题来解决原始问题。

对于约瑟夫环问题来说,递归的解法是通过递归地计算每轮的幸存者,直到只剩下一个人为止。

递归解法的关键是找到问题的递归关系。

具体而言,对于约瑟夫环问题,如果用f(n, m)表示n个人中最后幸存者的编号,那么可以得出如下的递归关系:f(n, m) = (f(n-1, m) + m) % n其中,%表示取模运算。

该递归关系表明,当有n个人的时候,最后幸存者的编号可以通过n-1个人的最后幸存者的编号计算得到。

2. 约瑟夫环问题的迭代解法除了递归解法之外,约瑟夫环问题还可以通过迭代的方式进行求解。

迭代解法的基本思路是模拟报数和杀人的过程,直到最后只剩下一个人为止。

迭代解法的关键是找到每一轮报数的规律。

具体而言,对于约瑟夫环问题,可以用一个循环队列来模拟报数的过程,每次报数到第m个人就将其从队列中移除。

通过不断循环这个过程,最终可以得到最后幸存者的编号。

3. 约瑟夫环问题的数学解法约瑟夫环问题还可以通过数学的方法进行求解。

具体而言,可以利用数学归纳法来推导出约瑟夫环问题的解析表达式。

这种方法的优点是可以更快地得到结果,但是需要一定的数学推导能力。

通过数学推导,可以得到约瑟夫环问题的解析表达式:f(n, m) = (f(n-1, m) + m) % n其中,f(1, m) = 0。

该表达式可以直接求解出最后幸存者的编号。

约瑟夫环问题详解

约瑟夫环问题详解

约瑟夫环问题详解很久以前,有个叫Josephus的⽼头脑袋被门挤了,提出了这样⼀个奇葩的问题:已知n个⼈(以编号1,2,3...n分别表⽰)围坐在⼀张圆桌周围。

从编号为k的⼈开始报数,数到m的那个⼈出列;他的下⼀个⼈⼜从1开始报数,数到m的那个⼈⼜出列;依此规律重复下去,直到圆桌周围的⼈全部出列这就是著名的约瑟夫环问题,这个问题曾经风靡⼀时,今天我们就来探讨⼀下这个问题。

这个算法并不难,都是纯模拟就能实现的。

思路⼀:⽤两个数组,mark[10000]和num[10000],mark这个数组⽤来标记是否出队,num这个⽤来存值,然后每次到循环节点的时候就判断mark是否为0(未出队),为0则输出num[i],并标记mark[i]=1,直到所有的num都出队。

附上C++代码:#include<iostream>#include<cstring>#include<cstdio>#include<cstdlib>#include<cctype>#include<cmath>#include<algorithm>using namespace std;char num[1000];bool mark[1000];int main(){while(true){memset(mark,0,sizeof(mark));int n;int k,m;int i,j;int del=k-1;cin>>n>>k>>m;for(i=0;i<n;++i){cin>>num[i];}int cnt=n;for(i=cnt;i>1;--i){for(int j=1;j<=m;){del=(del+1)%n;if(mark[del]==0)j++;}cout<<num[del]<<" ";mark[del]=1;}for(i=0;i<n;++i){if(mark[i]==0)break;}cout<<endl<<"The final winner is:"<<num[i]<<endl;}return 0;}思路⼆:⽤⼀个数组就可,每次到了循环节点了就将num[i]输出,然后将后⾯的值往前移动⼀位,直到所有的节点出列。

Josephu约瑟夫问题java实现(环形链表)

Josephu约瑟夫问题java实现(环形链表)

Josephu约瑟夫问题java实现(环形链表)5.4.1约瑟夫问题Josephu(约瑟夫、约瑟夫环) 问题为:设编号为 1,2,… n 的 n 个⼈围坐⼀圈,约定编号为 k(1<=k<=n)的⼈从 1 开始报数,数 到 m 的那个⼈出列,它的下⼀位⼜从 1 开始报数,数到 m 的那个⼈⼜出列,依次类推,直到所有⼈出列为⽌,由 此产⽣⼀个出队编号的序列。

5.4.2解决思路⽤⼀个不带头结点的循环链表来处理 Josephu 问题:先构成⼀个有 n 个结点的单循环链表,然后由 k 结点起从 1 开 始计数,计到 m 时,对应结点从链表中删除,然后再从被删除结点的下⼀个结点⼜从 1 开始计数,直到最后⼀个 尚硅⾕ Java 数据结构和算法 更多 Java –⼤数据 –前端 –python ⼈⼯智能 -区块链资料下载,可访问百度:尚硅⾕官⽹ 第 55页 结点从链表中删除算法结束。

代码实现//约瑟夫问题-环形链表public class Josepfu {public static void main(String[] args) {CircleSingleLinkedList circleSingleLinkedList = new CircleSingleLinkedList();circleSingleLinkedList.addBoy(5);// 加⼊ 5 个⼩孩节点circleSingleLinkedList.showBoy();circleSingleLinkedList.countBoy(1, 2, 5);}}//环形链表class CircleSingleLinkedList{//指向链表的第⼀个节点private Boy first = null;//添加num个⼩孩节点public void addBoy(int num){if (num<1)throw new RuntimeException("输⼊值错误");Boy curBoy = null;for(int i=1;i<=num;i++){Boy boy = new Boy(i);if (i==1){first = boy;first.setNext(first);//形成环curBoy = first;}else{boy.setNext(first);curBoy.setNext(boy);curBoy = boy;}}}// 根据⽤户的输⼊,计算出⼩孩出圈的顺序/**** @param startNo* 表⽰从第⼏个⼩孩开始数数* @param countNum* 表⽰数⼏下* @param nums* 表⽰最初有多少⼩孩在圈中*/public void countBoy(int startNo, int countNum, int nums) {if (nums<1||countNum<1||first==null||startNo<1||startNo>nums)throw new RuntimeException("参数有误,从新输⼊!!");//创建辅助指针,指向环形链表的最后⼀个节点Boy helper = first;while (helper.getNext()!=first){helper = helper.getNext();}//移动helper和first,使从第startNo个⼩孩开始数for (int i=0;i<(startNo-1);i++){helper = helper.getNext();first = first.getNext();}//开始数数,出圈while (helper!=first){//报数for (int i=0;i<(countNum-1);i++){helper = helper.getNext();first = first.getNext();}System.out.println("⼩孩"+ first.getNo() +"出队列:" );first = first.getNext();helper.setNext(first);}System.out.println("最后的⼩孩:"+ first.getNo());}//遍历环形链表public void showBoy(){if (first==null)throw new RuntimeException("链表为空");System.out.println("⼩孩的编号: "+first.getNo());//first⽆法移动,创建中介节点遍历链表Boy curBoy = first.getNext();//当中介节点再⼀次回到first时,表⽰链表遍历完成while (curBoy!=first){System.out.println("⼩孩的编号: "+curBoy.getNo()); curBoy = curBoy.getNext();}}}//创建boy类表⽰⼀个节点class Boy{private int no;private Boy next;public Boy(int no) {this.no = no;}public int getNo() {return no;}public void setNo(int no) {this.no = no;}public Boy getNext() {return next;}public void setNext(Boy next) {this.next = next;}}。

约瑟夫环问题 实验报告完整版

约瑟夫环问题 实验报告完整版
}
(2)约瑟夫环报数的算法在运行为循环方式,报数者除非本身已经出去,否则继续顺序报数,其报数循环的代码为
void Joseph(NODE *p,int number,int n)
{
int;=number; i++)
{
for(j=1; j<n-1; j++)
(2)基本要求
建立模型,确定存储结构。
对任意n个人,密码为m,实现约瑟夫环问题。
出圈的顺序可以依次输出,也可以用一个数组存储。
(3)思考:
采用顺序存储结构如何实现约瑟夫环问题?
如果每个人持有的密码不同,应如何实现约瑟夫环问题?
2.数据结构设计
由于约瑟夫环问题本身具有循环性质,考虑采用循环链表,为了统一对表中任意结点的操作,循环链表不带头结点。将循环链表的结点定义为如下结构类型:
5.运行测试与分析
(1)输出提示,如图1.2所示。
(2)根据提示,输入圈内人数n和每个人持有的密码m如图1.3所示。
(3)输出结果如图1.4所示
分析
6.实验收获及思考
通过该实验,我进一步增强了对于链表的理解,也对链表的操作和实现更为熟悉,熟练掌握了如何实现置空表、求表的长度、取结点、定位运算、插入运算、删除运算、建立不带头结点的单链表(头插入法建表)、建立带头结点的单链表(尾插入法建表),输出带头结点的单链表等操作。同时,锻炼了实际操作时的动手能力。
{
p=p->next;
}
q=p->next;
p->next=q->next;
p=p->next;
printf("第%3d个出圈的人是:%3d\n",i,q->value);

约 瑟 夫 环 问 题 的 三 种 解 法

约 瑟 夫 环 问 题 的 三 种 解 法

约瑟夫问题(数学解法及数组模拟)约瑟夫问题(有时也称为约瑟夫斯置换,是一个出现在计算机科学和数学中的问题。

在计算机编程的算法中,类似问题又称为约瑟夫环。

又称“丢手绢问题”.)据说著名犹太历史学家 Josephus有过以下的故事:在罗马人占领乔塔帕特后,39 个犹太人与Josephus及他的朋友躲到一个洞中,39个犹太人决定宁愿死也不要被敌人抓到,于是决定了一个自杀方式,41个人排成一个圆圈,由第1个人开始报数,每报数到第3人该人就必须自杀,然后再由下一个重新报数,直到所有人都自杀身亡为止。

然而Josephus 和他的朋友并不想遵从。

首先从一个人开始,越过k-2个人(因为第一个人已经被越过),并杀掉第k个人。

接着,再越过k-1个人,并杀掉第k个人。

这个过程沿着圆圈一直进行,直到最终只剩下一个人留下,这个人就可以继续活着。

问题是,给定了和,一开始要站在什么地方才能避免被处决?Josephus要他的朋友先假装遵从,他将朋友与自己安排在第16个与第31个位置,于是逃过了这场死亡游戏。

? 以上来自百度百科约瑟夫问题是个很有名的问题:N个人围成一个圈,从第一个人开始报数,第M个人会被杀掉,最后一个人则为幸存者,其余人都将被杀掉。

例如N=6,M=5,被杀掉的顺序是:5,4,6,2,3,1。

约瑟夫问题其实并不难,但求解的方法多种多样;题目的变化形式也很多。

接下来我们来对约瑟夫问题进行讨论。

1.模拟解法优点 : 思维简单。

?缺点:时间复杂度高达O(m*n)当n和m的值较大时,无法短时间内得到答案。

为了叙述的方便我们将n个人编号为:1- n ,用一个数组vis 来标记是否存活:1表示死亡 0表示存活 s代表当前死亡的人数? cnt 代表当前报了数的人数用t来枚举每一个位置(当tn时 t=1将人首尾相连)? 那么我们不难得出核心代码如下:bool vis[1000]; --标记当前位置的人的存活状态int t = 0; --模拟位置int s = 0; --死亡人数int cnt = 0; --计数器if(t n) t = 1;if(!vis[t]) cnt++; --如果这里有人,计数器+1if(cnt == m) --如果此时已经等于m,这这个人死去cnt = 0; --计数器清零s++; --死亡人数+1vis[t] = 1 --标记这个位置的人已经死去coutt" "; --输出这个位置的编号}while(s != n);接下来我们来看另一种更为高效快速的解法数学解法我们将这n个人按顺时针编号为0~n-1,则每次报数到m-1的人死去,剩下的人又继续从0开始报数,不断重复,求最后幸存的人最初的编号是多少?我们只需要将最后求得的解加1就能得到原来的编号。

Josephus环问题

Josephus环问题

Josephus环问题约瑟夫环问题问题描述:Josephus问题可以描述为如下的⼀个游戏:N个⼈编号从1到N,围坐成⼀个圆圈,从1号开始传递⼀个热⼟⾖,经过M次传递后拿着⼟⾖的⼈离开圈⼦,由坐在离开的⼈的后⾯的⼈拿起热⼟⾖继续进⾏游戏,直到圈⼦只剩下最后⼀个⼈。

例如:M=0,N=5,则游戏⼈依次被清除,5号最后留下;如果M=1,N=5,那么被清除的⼈的顺序是2,4,1,5,最后剩下的是3号。

如下是两种解题⽅法:建⽴⼀个N⼤⼩的数组,存储N个⼈是否还在圈⼦内(0为在圈⼦内,-1为已经离开圈⼦),依次循环遍历整个数组,直到剩下的⼈数(left)为1。

public static void pass(int m, int n){int[] num = new int[n];int left = n; //剩下的⼈数int index = 0; //当前遍历到的位置while(left != 1){for(int i = 0; i< m; i++){if(num[index++] != 0) //如果当前⼈已经清除i--;if(index >= n)index = 0;}while(num[index] != 0){index++;if(index >= n)index = 0;}System.out.println("out number is " + (index + 1));num[index] = -1; //将清除的数据下标置-1index++;if(index >= n)index = 0;left--;}}第⼆种⽅式,将1~N的数添加到⼀个ArrayList对列中,⾸先计算偏移量(mPrime),当mPrime⼩于对列长度的⼀半时,则从前往后依次遍历找到清除的元素;当mPrime⼤于对列长度的⼀半时,从后往前遍历找到清除的元素。

public static void pass2(int m, int n){ArrayList<Integer> list = new ArrayList<Integer>();for(int i = 1; i <= n; i++)list.add(i);ListIterator<Integer> iter = list.listIterator();int left = n; //剩下的⼈数int item = 0; //the out numberfor(int i= 1; i < n; i++){int mPrime = m % left;if(mPrime < left/2){ //如果当前的偏移量⼩于list长度的⼀半时if(iter.hasNext())item = iter.next();for(int j = 0; j < mPrime; j++){if(!iter.hasNext())iter= list.listIterator();item = iter.next();}}else{ //当偏移量⼤于list长度的⼀半时,从后往前找for(int j = 0; j< left - mPrime; j++){if(!iter.hasPrevious())iter = list.listIterator(list.size());item = iter.previous();}}System.out.println("out number is " + item);iter.remove();if(!iter.hasNext()) //有可能下⼀次循环mPrime就会⼩于left的⼀半iter = list.listIterator();left--;}}总结当M,N较⼤时,则第⼆种⽅法时间效率更⾼,实现表明,当N=100000,M=9000时(省略的两个算法中的syso语句),⽅法⼀个执⾏时间是30713ms,⽽第⼆种⽅法的执⾏时间仅为4891ms,M越⼤时⽅法⼀的时间效率会更差。

约瑟夫问题及变种

约瑟夫问题及变种

“约瑟夫”问题及若干变种例1、约瑟夫问题(Josephus)[问题描述]M只猴子要选大王,选举办法如下:所有猴子按1…M编号围坐一圈,从第1号开始按顺序1,2,…,N 报数,凡报到N的猴子退出到圈外,再从下一个猴子开始继续1~ N报数,如此循环,直到圈内只剩下一只猴子时,这只猴子就是大王。

M和N由键盘输入,1≤N,M≤10000,打印出最后剩下的那只猴子的编号。

例如,输入8 3,输出:7。

[问题分析1]这个例题是由古罗马著名史学家Josephus提出的问题演变而来的,所以通常称为Josephus(约瑟夫)问题。

在确定程序设计方法之前首先来考虑如何组织数据,由于要记录m只猴子的状态,可利用含m个元素的数组monkey来实现。

利用元素下标代表猴子的编号,元素的值表示猴子的状态,用monkey[k]=1表示第k只猴子仍在圈中,monkey[k]=0则表示第k只猴子已经出圈。

程序采用模拟选举过程的方法,设变量count表示计数器,开始报数前将count置为0,设变量current 表示当前报数的猴子编号,初始时也置为0,设变量out记录出圈猴子数,初始时也置为0。

每次报数都把monkey[current]的值加到count上,这样做的好处是直接避开了已出圈的猴子(因为它们对应的monkey[current]值为0),当count=n时,就对当前报数的猴子作出圈处理,即:monkey[current]:=0,count:=0,out:=out+1。

然后继续往下报数,直到圈中只剩一只猴子为止(即out=m-1)。

参考程序如下:program josephus1a {模拟法,用数组下标表示猴子的编号}const maxm=10000;var m,n,count,current,out,i:integer;monkey:array [1..maxm] of integer;beginwrite('Input m,n:');readln(m,n);for i:=1 to m do monkey[i]:=1;out:=0; count:=0; current:=0;while out<m-1 dobeginwhile count<n dobeginif current<m then current:=current+1 else current:=1;count:=count+monkey[current];end;monkey[current]:=0; out:=out+1; count:=0end;for i:=1 to m doif monkey[i]=1 then writeln('The monkey king is no.',i);readlnend.[运行结果]下划线表示输入Input m,n:8 3The monkey king is no.7 {时间:0秒}Input m,n:10000 1987The monkey king is no.8544 {时间:3秒}[反思]时间复杂度很大O(M*N),对于极限数据会超时。

用顺序表解决约瑟夫环问题

用顺序表解决约瑟夫环问题

⽤顺序表解决约瑟夫环问题⼀、实验题⽬:约瑟夫环问题:设编号为1,2,3,……,n的n(n>0)个⼈按顺时针⽅向围坐⼀圈,m为任意⼀个正整数。

从第⼀个⼈开始顺时针⽅向⾃1起顺序报数,报到m时停⽌并且报m的⼈出列,再从他的下⼀个⼈开始重新从1报数,报到m时停⽌并且报m的⼈出列。

如此下去,直到所有⼈全部出列为⽌。

要求设计⼀个程序模拟此过程,对任意给定的m和n,求出出列编号序列。

实验要求:⽤顺序表实现。

⼆、设计分析:⾸先创建了⼀个顺序表,并且⽤数组存每个⼈的编号,出列的时候将此编号置为0,代表此位置的⼈已经出列,循环查询编号不为0的元素,并⽤变量j记下当前是第⼏个⼈,当j==m的时候,代表此位置是第m个⼈,输出并将编号置为0,⽤k记录出列⼈的下⼀位的索引,并将数组长度-1,再进⾏新⼀轮的循环,直到所有⼈都出列。

三、程序代码:#include<iostream>#include<cstdlib>using namespace std;const int MaxSize=100;typedef struct Node{int data[MaxSize];int length;}List;void CreateList(List *&L,int n){L=(List *)malloc(sizeof(List*));for(int i=1;i<=n;i++){L->data[i]=i;}L->length=n;}void DispList(List *&L,int m){int len=L->length;int j=0,k=1;while(L->length){for(int i=1;i<=len;i++){if(L->data[k]){j++;}if(j==m){cout<<L->data[k]<<" ";L->data[k]=0;L->length--;j=0;k=(k+1)%(len+1)?(k+1)%(len+1):1;break;}k=(k+1)%(len+1)?(k+1)%(len+1):1;}}}int main(){int m,n;cin>>m>>n;List *L;CreateList(L,n);DispList(L,m);return 0;}。

  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

数据结构上机实验报告
02090401 12 雒文杰题目:约瑟夫环问题
一.问题描述
设有n个人围做一圈,现从某个人开始报数,数到m的人出列,接着从出列的下一个人开始重新报数,数到m的人又出列,如此下去,直到所有人都出列为止。

试设计确定他们的出列次序序列的程序。

二.基本要求
运用循环单链表解决约瑟夫环问题。

三.算法说明
本程序采用循环单链表的算法来解决约瑟夫环问题:建立一个循环单链表,按顺序查找指定结点,找到后删除,最后打印删除的编号序列。

四.源程序清单
#include <stdio.h>
#include <malloc.h>
#define n 7
#define NULL 0
struct clist
{int data,order; /* 人的序号, 密码*/
struct clist *next; /* 指向下一个节点的指针*/
};
typedef struct clist cnode;
typedef cnode *clink;
clink createclist(int *array,int len) /* 建立循环链表*/
{clink head;
clink before;
clink new_node;
int i;
head=(clink)malloc(sizeof(cnode));
if(!head)
return NULL;
head->data=array[0];
head->order=1;
head->next=NULL;
before=head;
for(i=1;i<len;i++)
{new_node=(clink)malloc(sizeof(cnode)); if(!new_node)
return NULL;
new_node->data=array[i];
new_node->order=i+1;
new_node->next=NULL;
before->next=new_node;
before=new_node;
}
new_node->next=head;
return head;
}
void main()
{clink head,ptr;
int find,j,i,a,list[n];
printf("Please input 'm'\n");
for(i=0;i<n;i++)
{scanf("%d",&list[i]);
printf("\n");
}
head=createclist(list,n);
printf("input first 's'\n");
scanf("%d",&a);
for(i=1;i<a-1;i++)
head=head->next;
ptr=head->next;
head->next=ptr->next;
find=ptr->data;
printf("order is %d\n",ptr->order);
free(ptr);
for(j=1;j<n;j++)
{for(i=1;i<find;i++)
head=head->next;
ptr=head->next;
head->next=ptr->next;
find=ptr->data;
printf("order is %d\n",ptr->order);
free(ptr); /* 让最后一个人也出列*/ }
}。

相关文档
最新文档