约瑟夫环问题讲解

合集下载

约 瑟 夫 环 问 题 的 三 种 解 法

约 瑟 夫 环 问 题 的 三 种 解 法

约瑟夫环的普通解法及优化约瑟夫环问题的普通解法很简单,就是不断遍历循环链表,删除节点,假如有n个人,等到第m个人报数时杀掉这个人,即删除这个节点,直到只剩下一个人。

struct NodeNode(int data):_data(data),_pNext(NULL)int _data;Node* _pNext;1,普通解法,时间复杂度为O(n*m),因为删掉一个节点需要遍历 m 次;代码实现Node* josephuskill1(Node* pHead,int m)--第m个人被杀掉if(pHead == NULL)return NULL;Node* pCur = pHead;while(pCur-_pNext)pCur = pCur-_pNext;pCur-_pNext = pHead;pCur = pHead;int num = m;Node* pRev;while(pCur-_pNext != pCur)--只剩下一个节点 while(--num)pRev = pCur;pCur = pCur-_pNext;pRev-_pNext = pCur-_pNext;--删除节点pHead = pCur;return pHead;测试代码void funtest1()Node node1(1);Node node2(2);Node node3(3);Node node4(4);Node node5(5);node1._pNext = node2;node2._pNext = node3;node3._pNext = node4;node4._pNext = node5;Node* ret = josephuskill1(node1,3);int main()funtest1();getchar();return 0;2.约瑟夫环的优化,时间复杂度为O(n)思路:我们直接直到最后存活的节点,然后删除其余节点,保存这个节点;问题是:我们怎样直接找到该节点呢?代码实现:int GetLive(int i,int m)--i是链表中总的节点个数,m是第m个报数的人被杀掉,返回存活的新的编号if(i == 1)--当链表中只剩下一个节点的时候,此时该节点对应的新编号就是1return 1;return (GetLive(i-1,m)+m-1)%i+1;--递归,当i=2时,我们得到存活节点(i=2)对应两个节点时的编号,直到i = N 时Node* josephuskill2(Node* pHead,int m)--第m个人被杀掉if(pHead == NULL)return NULL;Node* pCur = pHead;while(pCur-_pNext)pCur = pCur-_pNext;pCur-_pNext = pHead;int num = 1;pCur = pHead;while(pCur-_pNext != pHead)num++;--获取链表中节点的个数pCur = pCur-_pNext;int retnum = GetLive(num,m);--得到的retnum就是存活节点在总数为num个节点的编号--根据编号找到节点while(--retnum)pHead = pHead-_pNext;pHead-_pNext = pHead;return pHead;测试代码:void funtest1()Node node1(1);Node node2(2);Node node3(3);Node node4(4);Node node5(5);node1._pNext = node2;node2._pNext = node3;node3._pNext = node4;node4._pNext = node5;Node* ret = josephuskill2(node1,3);int main()funtest1();getchar();return 0;18 3、数到某个数(如:M)的时候,此人出列,下一个人重新报数结果表明,第一种循环链表的方式比第二种取模运算的方式要快,由于比例不是线性的,不能说是几倍,而且这个测试和python内部实现有关,换作C语言O3优化后结果就不一定一样了,所以测试结果不能说明什么哈~k k+1 k+2 . n-2, n-1, 0, 1, 2, . k-2f(N?1,M)表示,N-1个人报数,每报到M时杀掉那个人,最终胜利者的编号int cycle0(int n, int m){ -- 使用链表实现-- 若第 i 个人存活,则 men[i] == 0,否则 men[i] == 死亡序号CircularList CL=new CircularList();for (int i = 1; i = total; i++)循环语句do { g++; } while( g = N ) 可以用 for( ; g = N ; g++ ) {? }代替。

约 瑟 夫 环 问 题 的 三 种 解 法

约 瑟 夫 环 问 题 的 三 种 解 法

约瑟夫环问题python解法约瑟夫环问题:已知n个人(以编号1,2,3.n分别表示)围坐在一张圆桌周围。

从编号为k的人开始报数,数到k的那个人被杀掉;他的下一个人又从1开始报数,数到k的那个人又被杀掉;依此规律重复下去,直到圆桌周围的人只剩最后一个。

思路是:当k是1的时候,存活的是最后一个人,当k=2的时候,构造一个n个元素的循环链表,然后依次杀掉第k个人,留下的最后一个是可以存活的人。

代码如下:class Node():def __init__(self,value,next=None):self.value=valueself.next=nextdef createLink(n):return Falseif n==1:return Node(1)root=Node(1)tmp=rootfor i in range(2,n+1):tmp.next=Node(i)tmp=tmp.nexttmp.next=rootreturn rootdef showLink(root):tmp=rootwhile True:print(tmp.value)tmp=tmp.nextif tmp==None or tmp==root: def josephus(n,k):if k==1:print('survive:',n)root=createLink(n)tmp=rootwhile True:for i in range(k-2):tmp=tmp.nextprint('kill:',tmp.next.value) tmp.next=tmp.next.nexttmp=tmp.nextif tmp.next==tmp:print('survive:',tmp.value)if __name__=='__main__':print('-----------------')josephus(10,2)print('-----------------')josephus(10,1)print('-----------------')输出结果如下:-------------------------------------分界线-----------------------------------------感谢大家建议,第一种方法是直观暴力裸搞,确实不太简洁,下面写出我的第二种方法,求模来搞起,代码少了一些,如下:def josephus(n,k):if k==1:print('survive:',n)people=list(range(1,n+1))while True:if len(people)==1:p=(p+(k-1))%len(people)print('kill:',people[p])del people[p]print('survive:',people[0])if __name__=='__main__':josephus(10,2)josephus(10,1)运行结果和上面一样。

经典问题约瑟夫环讲解

经典问题约瑟夫环讲解

1.需求分析以无歧义的陈述说明程序设计的任务,强调的是程序要做什么?并明确规定:(1) 输入的形式和输入值的范围;输入的值为数字(2) 输出的形式;输出的形式为一串数字如:6,1,4,7,2,3,5(3) 程序所能达到的功能;编号为1,2,…,n的n个人按顺时针方向围坐一圈,每人持有一个密码(正整数)。

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

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

(4) 测试数据:包括正确的输入及其输出结果和含有错误的输入及其输出结果。

输入错误时一般不会显示结果。

总体归纳为:1.约瑟夫环(Joseph)问题的一种描述是:编号为1,2……,n的n个人按顺时针方向围坐一圈,每人持有一个密码(正整数)。

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

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

2.演示程序以用户和计算机的对话方式执行,即在计算机终端上显示“提示信息”之后,有用户在键盘上输入演示程序中规定的运算命令,相应的输入数据和运算结果显示在其后。

3.程序执行的命令包括:1)输入初始密码和人数 2)输入所有人的密码 3)显示输入的所有人的编号及相应的密码4)输出出列密码及编号 5)结束4.测试数据(1)m=20, n=7, 7个人的密码依次为3,1,7,2,4,8,4(2)m=7,n=20,20个人的密码依次为3,1,7,2,4,8,4(2)组数据为错误数据测程序的健壮性。

2.概要设计说明本程序中用到的所有抽象数据类型的定义、主程序的流程以及各程序模块之间的层次(调用)关系。

ADT LinearList{数据元素:D={ai| ai∈D0, i=1,2,…,n n≥0 ,D0为某一数据对象}关系:S={<ai,ai+1> | ai, ai+1∈D0,i=1,2, …,n-1}该程序只有主程序。

约瑟夫环问题

约瑟夫环问题

约瑟夫环问题问题描述:有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;有了这个公式,问题就变得多了。

约瑟夫环

约瑟夫环

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

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

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

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

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

求出列编号序列。

基本要求:需要基于线性表的基本操作来实现约瑟夫问题需要利用数组来实现线性表测试数据:输入:10,3输出:3 6 9 2 7 1 8 5 10 4选做内容:(1)使用单链表来实现之(2)使用循环链表来实现之实验中使用了数组以及链表的操作,来实现对约瑟夫环的解答。

一、先来讲一讲对数组的实现的思路:数组的实现是一种很常规的方法。

这里必须要设置一个临时变量t,在第一轮的循环中,t=0而且在这一轮中要出列的数字是(t+m-1)%i,举例来说:当n=10,m=3时,t=2,4,6,1,3,0,1,1,0,那么就可以知道最后剩下的是4了。

二、再来讲一讲链表的实现方式:在链表的改写中使用到了这几个函数:append(),next(),remove()和prve();这里实现的思路还是比较简单的。

当输入了n个数据后,使用append()函数连接起来,然后用一number来记录这个节点的代表次数。

实验一约瑟夫环问题

实验一约瑟夫环问题

实验四约瑟夫环问题一、问题描述设有编号为1,2,…n的n(n>0)个人围成一个圈.每个人持有个密码m。

从第1个人开始报数,报到m时停止报数,报m的人出圈,再从他的下个人起重新报数,报到m时停止报数,报m的出圈……如此下去,直到所有人全部出圈为止。

当任意给定n和m后,设计算法求n个人出圈的次序。

二、基本要求(1)建立模型,确定存储结构(2)对任意n个人,密码为m,实现约瑟夫环问题(3)出圈的顺序可以依次输出,也可以用一个数组存储三、实验步骤1.需求分析本演示程序用VC++编写,生成一个循环链表,输入人数输入密码,在执行报数输出操作,输出报数序列;①输入的形式和输入值的范围:输入的总共报数人数以及报数密码,在所有输入中,输入数据都为正整数。

②输出的形式:在输出报数顺序是输出报数顺序序号对应报数本身编号。

输出完成后输出是否重新进行提示信息。

③程序所能达到的功能:任意输入的报数人数people,及出列密码num输出报数出列顺序编号。

④测试数据:A.输入报数人数:50,初始化循环链表;B.输入报数密码:3,输出报数出列序列2.概要设计1)为了实现上述程序功能,需要定义循环链表的抽象数据类型:ADT LinkList {数据对象:D={ai|ai∈IntegerSet,i=0,1,2,…,n,n≥0}数据关系:R={<ai,ai+1>|ai,ai+1 ∈D}基本操作:creatcirlink(int n)操作结果:建立循环链表并进行初始化report_num(cirlink &hand,int m,int max)初始条件:循环链表hand已建立;操作结果:报数输出相应的元素data;Game()初始条件:循环链表hand已建立;操作结果:输入报数人people数及密码numMenu()操作结果:在屏幕上显示操作菜单}2)本程序包含4个函数:①主函数main()②显示操作菜单函数menu()③报数出列函数report_num ()④输入人数和密码函数Game()3.详细设计设计实现约瑟夫环问题的存储结构。

约瑟夫环问题小结

约瑟夫环问题小结

约瑟夫环问题⼩结⼀问题描述约瑟夫环问题的基本描述如下:已知n个⼈(以编号1,2,3...n分别表⽰)围坐在⼀张圆桌周围。

从编号为1的⼈开始报数,数到m的那个⼈出列;他的下⼀个⼈⼜从1开始报数,数到m的那个⼈⼜出列;依此规律重复下去,要求找到最后⼀个出列的⼈或者模拟这个过程。

⼆问题解法在解决这个问题之前,⾸先我们对⼈物进⾏虚拟编号,即相当于从0开始把⼈物重新进⾏编号,即⽤0,1,2,3,...n-1来表⽰⼈物的编号,最后返回的编号结果加上1,就是原问题的解(为什么这么做呢,下⽂有解释)。

⽽关于该问题的解通常有两种⽅法:1.利⽤循环链表或者数组来模拟整个过程。

具体来讲,整个过程很明显就可以看成是⼀个循环链表删除节点的问题。

当然,我们也可以⽤数组来代替循环链表来模拟整个计数以及出列的过程。

此处只给出利⽤数组来模拟这个过程的解法,最终结果为最后⼀个出列的⼈的编号:#include<iostream>#include<unordered_map>#include<queue>#include<cstring>#include<cstdlib>#include<cmath>#include<algorithm>#include<sstream>#include<set>#include<map>using namespace std;int main(){int n,m;cin>>n>>m;vector<int>rs(n);for(int i = 0 ; i < n; i++)rs[i] = i + 1;//对⼈物重新进⾏编号,从0开始int cur_index = 0;//当前圆桌状态下的出列⼈的编号int out_cnt = 0;//⽤以表⽰出列的⼈数int cnt = n;//表⽰当前圆桌的总⼈数while(out_cnt < n - 1)//当out_cnt等于n-1时,循环结束,此时圆桌师⽣最后⼀个⼈,即我们要的结果{if(cur_index + m > cnt){if((cur_index + m) % cnt == 0)//这种情况需要单独考虑,否则cur_index就变成负值了cur_index = cnt - 1;elsecur_index = (cur_index + m) % cnt - 1;}elsecur_index = cur_index + m - 1;cnt--;out_cnt++;cout<<"当前出列的为:"<<*(rs.begin() + cur_index)<<endl;rs.erase(rs.begin() + cur_index);//从数组中删去需要出队的⼈员}cout<<"最后⼀个出列的⼈物为:"<<rs[0]<<endl;}该⽅法的时间复杂度为O(nm),空间复杂度为O(n),整个算法的基本流程还是⽐较清晰的,相当于每次循环更新cur_cnt、cnt和out_cnt这三个变量,当out_cnt == n-1时,此时出队的⼈数⼀共有n-1⼈,圆桌上只剩下⼀个⼈了,停⽌循环。

约 瑟 夫 环 问 题 的 三 种 解 法

约 瑟 夫 环 问 题 的 三 种 解 法

约瑟夫环问题的简单解法(数学公式法)关于约瑟夫环问题,无论是用链表实现还是用数组实现都有一个共同点:要模拟整个游戏过程,不仅程序写起来比较烦,而且时间复杂度高达O(nm),当n,m非常大(例如上百万,上千万)的时候,几乎是没有办法在短时间内出结果的。

我们注意到原问题仅仅是要求出最后的胜利者的序号,而不是要读者模拟整个过程。

因此如果要追求效率,就要打破常规,实施一点数学策略。

为了讨论方便,先把问题稍微改变一下,并不影响原意:问题描述:n个人(编号0~(n-1)),从0开始报数,报到(m-1)的退出,剩下的人继续从0开始报数。

求胜利者的编号。

我们知道第一个人(编号一定是m%n-1) 出列之后,剩下的n-1个人组成了一个新的约瑟夫环(以编号为k=m%n的人开始): k k+1 k+2 … n-2, n-1, 0, 1, 2, … k-2并且从k开始报0。

现在我们把他们的编号做一下转换:k-2 – n-2k-1 – n-1解x’ —- 解为x注意x’就是最终的解变换后就完完全全成为了(n-1)个人报数的子问题,假如我们知道这个子问题的解:例如x是最终的胜利者,那么根据上面这个表把这个x变回去不刚好就是n个人情况的解吗?!!变回去的公式很简单,相信大家都可以推出来:x’=(x+k)%n如何知道(n-1)个人报数的问题的解?对,只要知道(n-2)个人的解就行了。

(n-2)个人的解呢?当然是先求(n-3)的情况—- 这显然就是一个倒推问题!下面举例说明:假设现在是6个人(编号从0到5)报数,报到(2-1)的退出,即 m=2。

那么第一次编号为1的人退出圈子,从他之后的人开始算起,序列变为2,3,4,5,0,即问题变成了这5个人报数的问题,将序号做一下转换:现在假设x为0,1,2,3,4的解,x’设为那么原问题的解(这里注意,2,3,4,5,0的解就是0,1,2,3,4,5的解,因为1出去了,结果还是一个),根据观察发现,x与x’关系为x’=(x+m)%n,因此只要求出x,就可以求x’。

约瑟夫环的详细解释

约瑟夫环的详细解释

约瑟夫环
约瑟夫环是一个数学的应用问题:
已知n个人(以编号1,2,3...n分别表示)围坐在一张圆桌周围。

从编号为k的人开始报数,数到m的那个人出列;他的下一个人又从1开始报数,数到m的那个人又出列;依此规律重复下去,直到圆桌周围的人全部出列。

程序输出出列顺序.
要通过输入n, k , m三个正整数,来求出列的序列
例如n=12, k=3,m=7时,出队序列为9 4 12 8 6 5 7 11 3 10 1 2
解决问题的核心步骤:
1.建立一个具有n个链结点,无头结点的循环链表
2.确定第1个报数人的位置
3.不断地从链表中删除链结点,直到链表为空
运行结果截图:。

约 瑟 夫 环 问 题 的 三 种 解 法

约 瑟 夫 环 问 题 的 三 种 解 法

Java 约瑟夫环问题的两种解法(循环数组,单向环形链表)Josephu 问题为:设编号为1,2,… n的n个人围坐一圈,约定编号为k(1=k=n)的人从1开始报数,数到m 的那个人出列,它的下一位又从1开始报数,数到m的那个人又出列,依次类推,直到所有人出列为止,由此产生一个出队编号的序列。

2.解决方法一:循环数组提示:每次报数,如果满足出圈的条件就将数组元素设置为-1,当下次报数时跳过-1。

直至数组最后一个元素变为-1,循环结束,数组的循环使用取模来完成。

--解决方法二:数组+取模public static void JosephuByArr(int total,int startNum,int m){ int []Arr=new int[total];int leave=total; --剩下的数量int count=0; --报数(0-1-2-3-4-5····)int index=startNum; --第一个元素--初始化数组(为了方便取模,最后一个元素放在数组的第0个,也可以先加一再取模)Arr[0]=total;for(int i=1;iArr.length;i++){Arr[i]=i;while(leave0){count++; --报数--找到报数为count的数组元素if(Arr[index%total]==-1){while(Arr[index%total]==-1){index++;--如果满足条件,输出(元素设置为-1)if(count%m==0){System.out.print(Arr[index%total]+"t");Arr[index%total]=-1;leave--;--下一个元素开始index++;3.解决方法二:单向环形链表提示:用一个不带头结点的循环链表来处理Josephu 问题:先构成一个有n个结点的单循环链表,然后由k结点起从1开始计数,计到m时,对应结点从链表中删除,然后再从被删除结点的下一个结点又从1开始计数,直到最后一个结点从链表中删除算法结束。

约 瑟 夫 环 问 题 的 三 种 解 法

约 瑟 夫 环 问 题 的 三 种 解 法

约瑟夫环问题的简单解法(数学公式法)关于约瑟夫环问题,无论是用链表实现还是用数组实现都有一个共同点:要模拟整个游戏过程,不仅程序写起来比较烦,而且时间复杂度高达O(nm),当n,m非常大(例如上百万,上千万)的时候,几乎是没有办法在短时间内出结果的。

我们注意到原问题仅仅是要求出最后的胜利者的序号,而不是要读者模拟整个过程。

因此如果要追求效率,就要打破常规,实施一点数学策略。

为了讨论方便,先把问题稍微改变一下,并不影响原意:问题描述:n个人(编号0~(n-1)),从0开始报数,报到(m-1)的退出,剩下的人继续从0开始报数。

求胜利者的编号。

我们知道第一个人(编号一定是m%n-1) 出列之后,剩下的n-1个人组成了一个新的约瑟夫环(以编号为k=m%n的人开始):k k+1 k+2 … n-2, n-1, 0, 1, 2, … k-2并且从k开始报0。

现在我们把他们的编号做一下转换:k-2 – n-2k-1 – n-1解x’ —- 解为x注意x’就是最终的解变换后就完完全全成为了(n-1)个人报数的子问题,假如我们知道这个子问题的解:例如x是最终的胜利者,那么根据上面这个表把这个x变回去不刚好就是n个人情况的解吗?!!变回去的公式很简单,相信大家都可以推出来:x’=(x+k)%n如何知道(n-1)个人报数的问题的解?对,只要知道(n-2)个人的解就行了。

(n-2)个人的解呢?当然是先求(n-3)的情况—- 这显然就是一个倒推问题!下面举例说明:假设现在是6个人(编号从0到5)报数,报到(2-1)的退出,即 m=2。

那么第一次编号为1的人退出圈子,从他之后的人开始算起,序列变为2,3,4,5,0,即问题变成了这5个人报数的问题,将序号做一下转换:现在假设x为0,1,2,3,4的解,x’设为那么原问题的解(这里注意,2,3,4,5,0的解就是0,1,2,3,4,5的解,因为1出去了,结果还是一个),根据观察发现,x与x’关系为x’=(x+m)%n,因此只要求出x,就可以求x’。

约瑟夫环问题及注释

约瑟夫环问题及注释

约瑟夫环问题及程序问题描述:编号是1,2,……n的n个人按照顺时针方向围坐一圈。

现在从第s个人开始顺序报数,数到第m的人出列,然后从出列的下一个人重新开始报数,数到第m的人又出列,……如此反复,直到所有的人都出列为止。

解决思路:(1)首先利用线性表的一些运算如创建空线性表、插入元素等构造Josephus表。

(2)从Josephus表中的第s个结点开始寻找、输出和删除表中的第m个结点,然后再从该结点的下一个结点开始寻找、输出和删除表中的第m个结点,重复此过程,直到表中的所有元素都删除。

算法基本思想:用整数i来代替n i,将初始序列改写成一个整数的序列:1,2,3,……,n,并把它们存储在一个palist所指向的顺序表中,当s<=n时,第s个人放在palist->elem[s-1]之中,因此第一个报数出列的应该是下标为s-1+m-1对n取模后的元素,如果这个下标为i,出列工作只要将palist->elem[i]从顺序表中删除,然后对palist->elem[0],palist->elem[1],……,palist->elem[n-2]从下标i开始重复上述过程。

程序清单:#include<stdio.h>#include<stdlib.h>#define FALSE 0#define TRUE 1typedef int DataType;struct SeqList{int MAXNUM;int n;DataType *elem;};typedef struct SeqList *PSeqList;PSeqList createNullList_seq(int m){PSeqList palist=(PSeqList)malloc(sizeof(struct SeqList));if(palist!=NULL){palist->elem=(DataType *)malloc(sizeof(DataType)*m);if(palist->elem){palist->MAXNUM=m;palist->n=0;return palist;}elsefree(palist);}printf("Out of space!!\n");return NULL;}int isNullList_seq(PSeqList palist){return(palist->n==0);}int locate_seq(PSeqList palist,DataType x){int q;for(q=0;q<palist->n;q++)if(palist->elem[q]==x)return(q);return -1;}int insertPre_seq(PSeqList palist,int p,DataType x){int q;if(palist->n>=palist->MAXNUM){printf("Overflow!\n");return 0;}if(isNullList_seq(palist)){palist->elem[0]=x;palist->n=1;return 1;}if(p<0||p>palist->n){printf("Not exist!\n");return 0;}for(q=palist->n-1;q>=p;q--)palist->elem[q+1]=palist->elem[q];palist->elem[p]=x;palist->n=palist->n+1;return 1;}int deleteP_seq(PSeqList palist,int p){int q;if(p<0||p>palist->n-1){printf("Not exist!\n");return 0;}for(q=p;q<palist->n-1;q++)palist->elem[q]=palist->elem[q+1];palist->n=palist->n-1;return 1;}void josephus_seq(PSeqList palist,int s,int m){int s1,i,w;s1=s-1;for(i=palist->n;i>0;i--){s1=(s1+m-1)%i;w=palist->elem[s1];printf("Out element %d\n",w);deleteP_seq(palist,s1);}}main(){PSeqList jos_alist;int i;int n,s,m;printf("\n please input the values(<100) of n=");scanf("%d",&n);printf("please input the values of s=");scanf("%d",&s);printf("please input the values of n=");scanf("%d",&m);jos_alist=createNullList_seq(n);if(jos_alist!=NULL){for(i=0;i<n;i++)insertPre_seq(jos_alist,i,i+1);josephus_seq(jos_alist,s,m);free(jos_alist->elem);free(jos_alist);}}运行结果:#include<stdio.h>#include<stdlib.h>#define FALSE 0#define TRUE 1typedef int DataType;//定义datatype为int型struct SeqList //定义链表结构体{int MAXNUM; //报数的最大值int n; //链表中结点个数DataType *elem; //一个指向数组的指针};typedef struct SeqList *PSeqList;PSeqList createNullList_seq(int m) //创建空表{PSeqList palist=(PSeqList)malloc(sizeof(struct SeqList));//定义结构体指针if(palist!=NULL){palist->elem=(DataType *)malloc(sizeof(DataType)*m);//给elme开空间if(palist->elem){palist->MAXNUM=m;palist->n=0;return palist;}elsefree(palist);}printf("Out of space!!\n");return NULL;}int isNullList_seq(PSeqList palist)//判断链表是否为空{return(palist->n==0);}int locate_seq(PSeqList palist,DataType x)//返回x节点的位置{int q;for(q=0;q<palist->n;q++)if(palist->elem[q]==x)return(q);return -1;}int insertPre_seq(PSeqList palist,int p,DataType x)//插入x,到位置p上{int q;if(palist->n>=palist->MAXNUM){printf("Overflow!\n");return 0;}if(isNullList_seq(palist)){palist->elem[0]=x;palist->n=1;return 1;}if(p<0||p>palist->n){printf("Not exist!\n");return 0;}for(q=palist->n-1;q>=p;q--)palist->elem[q+1]=palist->elem[q];palist->elem[p]=x;palist->n=palist->n+1;return 1;}int deleteP_seq(PSeqList palist,int p){int q;if(p<0||p>palist->n-1){printf("Not exist!\n");return 0;}for(q=p;q<palist->n-1;q++)palist->elem[q]=palist->elem[q+1];//让p后面的每一个节点前移一位palist->n=palist->n-1;return 1;}void josephus_seq(PSeqList palist,int s,int m)//最重要的函数,按顺序输出出队的号{int s1,i,w;s1=s-1;for(i=palist->n;i>0;i--){s1=(s1+m-1)%i;//这个关系式,可以得出要出队的人的位置w=palist->elem[s1];printf("Out element %d,,s1=%d,,i=%d,,m=%d\n",w,s1,i,m);deleteP_seq(palist,s1);//删除s1节点}}main(){PSeqList jos_alist;int i;int n,s,m;printf("\n please input the values(<100) of n=");scanf("%d",&n);printf("please input the values of s=");scanf("%d",&s);printf("please input the values of m=");scanf("%d",&m);jos_alist=createNullList_seq(n);if(jos_alist!=NULL){for(i=0;i<n;i++)insertPre_seq(jos_alist,i,i+1);josephus_seq(jos_alist,s,m);free(jos_alist->elem);free(jos_alist);}}。

约瑟夫环问题的三种解法

约瑟夫环问题的三种解法

约瑟夫环问题的三种解法约瑟夫问题是个著名的问题: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位幸存者。

约瑟夫环问题(Josephus)

约瑟夫环问题(Josephus)

• 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.数据和结果显示:
问题描述
• 已知n(<2^15)个人(以编号1,2,…,n 分别表示)围坐在一圆桌上,从编号为k (1≤ k≤ n)的人开始报数,数到m的那个 人出列,他的下一个人又从1开始报数,数 到m的那个人又出列,依此重复,直到圆桌 周围的人全部出列,依次输出最后个整数T(<2^15)表示测 试次数,接着第二到T+1行分别为n,m和k 的值。 例:2 10 2 3
• public void SortArray(int[]a,int n,int m,int k,int g){ • int[] b=new int[n]; • int c=0; • int i=k-2; • while(true){ • for(int j=0;j<m;){ • i=(i+1)%n; • if(a[i]!=0){ • j++; • } • }
总人数n 起始号码k 循环数m 最后出列的3人 总人数n 起始号码k 5 2 2 2 1 4 20 6
数据测试
总人数n 起始号码k 循环数m 最后出列的3人 总人数n 起始号码k 循环数m 最后出列的3人
10 2 3 6 1 5 30 4 7 18 23 26
循环数m
最后出列的3人
8
3 14 6
数据测试
最后出列的3人 总人数n

约 瑟 夫 环 问 题 的 三 种 解 法

约 瑟 夫 环 问 题 的 三 种 解 法

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

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

又称“丢手绢问题”.)据说著名犹太历史学家 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就能得到原来的编号。

约瑟夫环问题(最简单的数学解法)

约瑟夫环问题(最简单的数学解法)

约瑟夫环问题(最简单的数学解法)基本问题描述:已知n个⼈(以编号1,2,3...n分别表⽰)围坐在⼀张圆桌周围。

从编号为1的⼈开始报数,数到m的那个⼈出列;他的下⼀个⼈⼜从1开始报数,数到m的那个⼈⼜出列;依此规律重复下去,直到圆桌周围的⼈全部出列。

(也类似于变态杀⼈狂问题)通常解决这类问题时我们把编号从0~n-1,最后结果+1即为原问题的解。

通常,我们会要求输出最后⼀位出列的⼈的序号。

那么这⾥主要研究的是最后⼀个出列的⼈的序号要怎么确定。

当n,m数据量很⼩的时候,我们可以⽤循环链表模拟约瑟夫环的过程。

当模拟到⼈数等于1的时候,输出剩下的⼈的序号即可。

这种⽅法往往实现起来⽐较简单,⽽且也很容易理解。

但是时间复杂度却是很糟糕的,达到了O(n m),这样的话,其实在n,m⽐较⼤的时候(n m达到10^8或者更⼤),那么要得出结果往往需要耗费很长的时间,但是我们可以运⽤⼀点数学上的技巧,将最后结果推导出来。

为了简化出列的过程:⾸先我们把这n个⼈的序号编号从0~n-1(理由很简单,由于m是可能⼤于n的,⽽当m⼤于等于n时,那么第⼀个出列的⼈编号是m%n,⽽m%n是可能等于0的,这样编号的话能够简化后续出列的过程),当数到m-1的那个⼈出列,因此我们编号完成之后,开始分析出列的过程:第⼀次出列:⼀开始的时候,所有⼈的编号排成序列的模式即为:0,1,2,3,4,5...n-2,n-1那么第⼀次出列的⼈的编号则是(m-1)%n1,那么在第⼀个⼈出列之后,从他的下⼀个⼈⼜开始从0开始报数,为了⽅便我们设k1 =m%n1(n1为当前序列的总⼈数)那么在第⼀个⼈出列之后,k1则是下⼀次新的编号序列的⾸位元素,那么我们得到的新的编号序列为:k1,k1+1,k1+2,k1+3...n-2,n-1,0,1,2...k1-3,k1-2 (k1-1第⼀次已出列)那么在这个新的序列中,第⼀个⼈依旧是从0开始报数,那么在这个新的序列中,每个⼈报的相应数字为:0,1,2,3....n-2那么第⼆次每个⼈报的相应数字与第⼀次时⾃⼰相应的编号对应起来的关系则为:0 --> k11 --> k1+12 --> k1+2...n-2 ---> (k1+n-2)%n1(n1为当前序列的总⼈数,因为是循环的序列,k1+n-1可能⼤于总⼈数)那么这时我们要解决的问题就是n-1个⼈的报数问题(即n-1阶约瑟夫环的问题)可能以上过程你还是觉得不太清晰,那么我们重复以上过程,继续推导剩余的n-1个⼈的约瑟夫环的问题:那么在这剩下的n-1个⼈中,我们也可以为了⽅便,将这n-1个⼈编号为:0,1,2,3,4...n-2那么此时出列的⼈的编号则是(m-1) % n2(n2为当前序列的总⼈数),同样的我们设k2 = m % n2,那么在这个⼈出列了以后,序列重排,重排后新的编号序列为:k2,k2+1,k2+2,k2+3...n-2,n-1,0,1,2...k2-3,k2-2 (k2-1第⼀次已出列)那么在这个新的序列中,第⼀个⼈依旧是从1开始报数,那么在这个新的序列中,每个⼈报的相应数字为:1,2,3,4....n-2那么这样的话是不是⼜把问题转化成了n-2阶约瑟夫环的问题呢?后⾯的过程与前两次的过程⼀模⼀样,那么递归处理下去,直到最后只剩下⼀个⼈的时候,便可以直接得出结果当我们得到⼀个⼈的时候(即⼀阶约瑟夫环问题)的结果,那么我们是否能通过⼀阶约瑟夫环问题的结果,推导出⼆阶约瑟夫环的结果呢?借助上⾯的分析过程,我们知道,当在解决n阶约瑟夫环问题时,序号为k1的⼈出列后,剩下的n-1个⼈⼜重新组成了⼀个n-1阶的约瑟夫环,那么假如得到了这个n-1阶约瑟夫环问题的结果为ans(即最后⼀个出列的⼈编号为ans),那么我们通过上述分析过程,可以知道,n阶约瑟夫环的结果(ans + k)%n(n为当前序列的总⼈数),⽽k = m%n则有:n阶约瑟夫环的结果(ans + m % n)%n,那么我们还可以将该式进⾏⼀下简单的化简:当m<n时,易得上式可化简为:(ans + m)% n⽽当m>=n时,那么上式则化简为:(ans % n + m%n%n)% n即为:(ans % n + m%n)% n⽽(ans + m)% n = (ans % n + m%n)% n因此得证(ans + m % n)%n = (ans + m)% n这样的话,我们就得到了递推公式,由于编号是从0开始的,那么我们可以令f[1] = 0; //当⼀个⼈的时候,出队⼈员编号为0f[n] = (f[n-1] + m)%n //m表⽰每次数到该数的⼈出列,n表⽰当前序列的总⼈数⽽我们只需要得到第n次出列的结果即可,那么不需要另外声明数组保存数据,只需要直接⼀个for循环求得n阶约瑟夫环问题的结果即可由于往往现实⽣活中编号是从1-n,那么我们把最后的结果加1即可。

约瑟夫环问讲解

约瑟夫环问讲解

用指针实现
• 定义两个数列:一个表示队列(person),一个存放出列顺序(pout) • 先把数组person赋值(0表示已出列)
• 找出出列顺序 • 用temp表示循环计数 • 用指针p的移动计算出列顺序 • 1.temp在0到m间循环, • 指针p在数组上随着temp的增加而移动 • 当temp等于m时,标记指针所指向的值为0,表示该元素已出列m-• 把已出列的元素移动到数组po数组 • 2.当指针移动到队尾的时候,指针重新指向开头 • 当指针指向已出列的元素(值为0)时,指针向后移动temp不增加 • • 打印po得到出列顺序
{ //指向队尾时 //指针重新回到头
if(p==(pp+n)) p=pp ; else { p=p+1 ; }
temp=temp+1 ;
} else { if(p==(pp+n))
• //如果到达队尾,指针重 • • • • • •
新回到队头 p=pp ; p=p+1; //跳过出列的元素 } } p=p-1 ; //队列长度减一 po[i]=*p ; //生成输出队列顺序 *p=0 ; //标记成已经出队 } }
我们可以把点名看成是不重复的,比如,有N个人,第一 轮后剩N1个人、第二轮剩N2个人……以此类推;那么, 第二轮的地M个人可以看做第N+M次被点名。因此被淘汰 的总是能被3整除的。
如何实现最后一位数完又从第一位开始 数的问题?
很简单,我们可以用模运算(%)来完成。 比如: 1%3=1; 2%3=2; 3%3=0; 4%3=1。 因此我们可以用 j=i%n+1 (j表示人的序号,n表示场上还剩的人数,i表示点名次数) 来实现上述循环

• •

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越⼤时⽅法⼀的时间效率会更差。

简单的约瑟夫环算法

简单的约瑟夫环算法

简单的约瑟夫环算法约瑟夫环问题起源于⼀个犹太故事。

约瑟夫环问题的⼤意如下: 罗马⼈攻占了桥塔帕特,41个⼈藏在⼀个⼭洞中躲过了这场浩劫。

这41个⼈中,包括历史学家Josephus(约瑟夫)和他的⼀个朋友。

剩余的39个⼈为了表⽰不向罗马⼈屈服,决定集体⾃杀。

⼤家制定了⼀个⾃杀⽅案,所有这41个⼈围成⼀个圆圈,由第⼀个⼈开始顺时针报数,每报数为3的⼈就⽴刻⾃杀,然后再由下⼀个⼈重新开始报数,仍然是每报数为3的⼈就⽴刻⾃杀......,直到所有的⼈都⾃杀⾝亡为⽌。

约瑟夫和他的朋友并不想⾃杀,于是约瑟夫想到了⼀个计策,他们两个同样参与到⾃杀⽅案中,但是最后却躲过了⾃杀。

请问,他们是怎么做到的?package .datastruct;import java.util.Scanner;//简单的约瑟夫环求解public class Josephus {static final int Num=41; //总⼈数static final int KillMan=3; //⾃杀者报数//约瑟夫环算法static void josephus(int alive){int []man = new int[Num];int count=1;int i=0,pos=-1;while(count<=Num){do{pos=(pos+1)%Num; //环处理if(man[pos]==0) //只有没⾃杀的⼈才不等于0i++;if(i==KillMan){ //该⼈⾃杀i=0;break;}}while(true);man[pos]=count;System.out.printf("第%2d个⼈⾃杀!约瑟夫环编号为%2d",pos+1,man[pos]);if(count%2==1){System.out.printf("->");}else{System.out.printf("->\n"); //输出换⾏}count++;}System.out.println();System.out.printf("这%d个需要存活的⼈的初始位置应该排在以下序号:\n",alive);alive = Num - alive;for(i=0;i<Num;i++){if(man[i]>alive)System.out.printf("初始编号:%d,约瑟夫环编号:%d\n", i+1,man[i]);}System.out.println();}public static void main(String[] args){int alive;System.out.print("约瑟夫环问题求解!\n");System.out.print("请输⼊需要留存的⼈的数量:");Scanner input = new Scanner(System.in);alive = input.nextInt();josephus(alive);}}。

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

2009年02月24日星期二下午 05:03问题描述:约瑟夫环问题;有N个人围成一个环,从第一个人开始报数,报到M 的人退出环,并且由他的M值来代替原有的M值,要求输出离开环的顺序。

#include<iostream>#include<stdlib.h>using namespace std;//结点中数据域的类型定义typedef struct{int number;//标号int chipher;//手中的值}DataType;//带头结点的单循环链表typedef struct node{DataType data;struct node *next;}Scnode;//初始化void MyInit(Scnode **head)//指向指针的指针。

{if((*head=(Scnode *)malloc(sizeof(Scnode)))==NULL) exit(1);//动态分配 (*head)->next=*head;}//插入int MyInsert(Scnode *head,int i,DataType x){Scnode *p,*q;int j;p=head->next;j=1;while(p->next!=head&&j<i-1){p=p->next;j++;}if(j!=i-1&&i!=1){cout<<"erro!!!!!!!!!";return 0;}if((q=(Scnode *)malloc(sizeof(Scnode)))==NULL) exit(1); q->data=x;q->next=p->next;p->next=q;return 1;}//删除int MyDelete(Scnode *head,int i,DataType *x){Scnode *p,*q;int j;p=head;j=1;while(p->next!=head&&j<i-1){p=p->next;j++;}if(j!=i-1){cout<<"erro!!!!!!!!!";return 0;}q=p->next;*x=q->data;p->next=p->next->next;free(q);return 1;}//取数据元素int MyGet(Scnode *head,int i,DataType *x){Scnode *p;int j;p=head;j=0;while(p->next!=head&&j<i){p=p->next;j++;}if(j!=i){cout<<"erro!!!!!!!!!";return 0;}*x=p->data;return 1;}//判断是否为空int MyNotEmpty(Scnode *head){if(head->next==head) return 0;else return 1;}//删除P结点所指结点的下一个结点(也就是下面函数中的pre结点的下一个结点) void MyDelete(Scnode *p){Scnode *q=p->next;p->next=p->next->next;free(q);}//关键的函数void MyRing(Scnode *head,int m){Scnode *pre,*curr;int i;pre=head;curr=head->next;while(MyNotEmpty(head)==1)//这个喜欢是外层的把人循环完{for(int i=1;i<m;i++)//注意此处的循环是把当前m值下的人找出来。

{pre=curr;curr=curr->next;if(curr==head)//防止curr结点指向head,为什么呢,因为curr=curr-next移动过程中是要计数的{pre=curr;curr=curr->next;}}cout<<" "<<curr->data.number;m=curr->data.chipher;//这里重新赋值到新的密码m,注意此处的写法curr->data.chiphercurr=curr->next;//从下一个结点开始计数if(curr==head) curr=curr->next;//问题同上。

MyDelete(pre);//删除被指定到的结点}cout<<endl;}void main(){DataType test[7]={{1,3},{2,1},{3,7},{4,2},{5,4},{6,8},{7,4}};int n=7,m=20,i;Scnode *head;MyInit(&head);for(i=1;i<=n;i++)MyInsert(head,i,test[i-1]);MyRing(head,m);}数据结构:约瑟夫环问题的求解,链表数组,简单高效2008-03-25 14:59约瑟夫问题:12个人排成一圈,从1号报数,凡是数到5的人就走出队列(出局),然后继续报数,求最后一个出局的人。

编号为1,2,......,n的n个人按照顺时针方向围坐一圈。

从第一个人开始顺时针方向自1开始报数,报到m时停止报数。

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

方法1:/*** 名称:** 描述:** Copyright: Copyright 2008* 创建日期 Mar 25, 2008* 作者 zhangtianshun* E-mail zhangts8888@* 版本 1.0*/public class JosephCircle {public JosephCircle() {// TODO Auto-generated constructor stub}public static int josephCircle(int totalNum, int perNum) { int count[] = new int[totalNum];for (int i = 0; i < count.length; i++) {count[i] = i + 1;}int flag = 0;int k = 0, n = 0;while (flag != 11) {for (k = 0; k < count.length; k++) {if (count[k] != 0) {n++;if (n % perNum == 0) {count[k] = 0;flag++;}}}}for (k = 0; k < count.length; k++) {if (count[k] != 0) {n = count[k];break;}}return n;}/*** @param args*/public static void main(String[] args) {// TODO Auto-generated method stubint total = 12;int per = 5;JosephCircle joseph = new JosephCircle(); int last = -1;last = joseph.josephCircle(total, per);System.out.println("最后出列人的编号是: " + last); }}方法2:/*** 名称:** 描述:** Copyright: Copyright 2008* 创建日期 Mar 25, 2008* 作者 zhangtianshun* MSN zhangts8888@* E-mail zhangts8888@* 版本 1.0*/public class Josephus {/*** 约瑟夫环的求解思路:最简单的方法是用循环链表实现.* 现在用数组实现链表的效果.数组下标+1为数字编号,* 数组的值为下一个数组的地址.这样实现了链表的效果.* 当报数count=step-1时,删除下一个节点,节点值赋为-1. * 以此循环,直到数组元素都值为-1,最后删除的即为结果. */public Josephus() {super();}/*** 约瑟夫问题的求解* @param array 待处理的整形数组* @param size 待处理的数组长度* @param step 步长* @return Int 最后出列人的编号.*/public int joseph(int array[],final int size,final int step) { int flag = -1;//初始化数组链表,数组元素的值为下一个节点下标//即为: 1->2->3......(size-1)->0for(int i=0;i<size-1;i++)array[i] = i+1;array[size-1] = 0;/*** intRemain 当前剩下的有效节点* intDeleted 最好删除的一个节点的下班* intCurrent 当前数组元素的下标值* intCount 计数器*/int intRemain,intDeleted,intCurrent,intCount; intRemain = size;intDeleted = -1;intCurrent = 0;intCount = 0;do{//当前节点有效的标准是:不等于-1(flag)if(array[intCurrent] != flag){intCount++;//删除一个节点if(step - intCount == 1){//删除后一个节点,主要是将当前节点的向量指向下一个节点的下一个节点intDeleted = array[intCurrent];array[intCurrent] = array[intDeleted];array[intDeleted] = -1;intRemain--;intCount = 0;}}//当删除最好一个节点元素后,intCurrent的值变为-1.这之后数组就不能再被访问了. intCurrent = array[intCurrent];}while(intRemain !=0 );return intDeleted +1 ;}/*** @param args*/public static void main(String[] args) {// TODO Auto-generated method stubchar num = '7';System.out.println((int)num);int array[] = new int[12];//人数int size = 12;//出局的倍数int step = 5;int last = -1;Josephus ysf = new Josephus();last = ysf.joseph(array, size, step);System.out.println("最后出列人的编号是: "+last);单循环链表解决约瑟夫环问题]约瑟夫问题的:编号为1,2,....,N的N个人按顺时针方向围坐一圈,每人持有一个密码(正整数),一开始任选一个正整数作为报数上限值M,从第一个人开始按顺时针方向自1开始顺序报数,报到M时停止报数。

相关文档
最新文档