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

合集下载

约瑟夫问题与循环链表解决环状数据结构的应用问题

约瑟夫问题与循环链表解决环状数据结构的应用问题

约瑟夫问题与循环链表解决环状数据结构的应用问题约瑟夫问题是一个经典的数学问题,涉及到环状数据结构的应用。

在这个问题中,有n个人围成一圈,从第一个人开始依次报数,每报到第m个人就出列,直到剩下最后一个人。

本文将介绍约瑟夫问题的背景和解决方法,并探讨循环链表在解决环状数据结构问题中的应用。

一、约瑟夫问题的背景约瑟夫问题最早可追溯到两千多年前。

根据传说,犹太历史学家弗拉维奥·约瑟夫斯(Flavius Josephus)当时被罗马军队围困在一个洞穴中,他和其他39个犹太人决定宁愿自杀也不被俘。

他们决定站成一个圆圈,从第一个人开始,数到第三个人就将他杀死,直到最后一个人留下来。

通过解决这个例子中的问题,人们发现了一种递推模式,即每次被杀的人索引号是前一个被杀人索引号加上一个特定数(m)对人数(n)取余的结果。

二、求解约瑟夫问题1. 暴力法最简单的解法是通过一个数组,将每个人标记为存活或死亡。

然后从第一个人开始,按照指定的m值遍历循环,找到下一个存活的人,直到只剩下最后一个人。

这种方法的问题在于时间复杂度较高,特别是当人数和m值较大时,算法运行效率低下。

2. 循环链表循环链表是解决约瑟夫问题的一种有效数据结构。

在循环链表中,每个节点都包含一个指向下一个节点的指针,最后一个节点指向第一个节点,形成一个闭环。

通过使用循环链表,我们可以轻松地实现约瑟夫问题的求解。

首先,我们将n个人作为节点插入到循环链表中,并将最后一个节点指向第一个节点。

然后,我们从第一个人开始依次报数,每报到第m个人,就将该节点从链表中移除。

此时,链表的结构会自动调整,继续报数直到只剩下最后一个人。

这种方法只需要遍历一次链表,因此时间复杂度为O(n)。

相比暴力法,它的运行效率更高。

三、循环链表解决环状数据结构的应用问题除了约瑟夫问题,循环链表还可以应用于其他环状数据结构的解决方案中。

1. 环形队列环形队列是计算机科学中常用的一种数据结构,它可以实现缓冲区的循环利用。

实验报告 约瑟夫问题

实验报告 约瑟夫问题
pNew->next = pCur->next;
pCur->next = pNew;
pCur = pNew;
printf("结点%d,密码%d\n",pCur->id, pCur->cipher);
}
}
printf("完成单向循环链表的创建!\n");
}
(3)运行"约瑟夫环"问题
static void StartJoseph(NodeType **, int)
exit(-1);
}
pNew->id = iId;
pNew->cipher = iCipher;
pNew->next = NULL;
return pNew;
}
(6)测试链表是否为空,空为TRUE,非空为FALSE
static unsigned EmptyList(const NodeType *pHead)
实验内容
利用循环链表实现约瑟夫环求解。
实验说明
1.问题描述
约瑟夫问题的:编号为1,2,....,N的N个人按顺时针方向围坐一圈,每人持有一个密码(正整数),一开始任选一个正整数作为报数上限值M,从第一个人开始按顺时针方向自1开始顺序报数,报到M时停止报数。报M的人出列,将他的密码作为新的M值,从他在顺时针方向上的下一个人开始重新从1报数,如此下去,直至所有人全部出列为止。试设计一个程序求出出列顺序。
{
if(!pHead)
{
return TRUE;
}
return FALSE;
}
实验中遇到的问题及解决方法
实验结果如下:
实验总结(结果和心得体会)

约瑟夫环问题

约瑟夫环问题

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

约瑟夫环问题源代码(C语言)

约瑟夫环问题源代码(C语言)

约瑟夫环问题如下:已知n个人(n>=1)围桌一园桌周围,从1开始顺序编号。

从序号为1的人开始报数,顺时针数到m的那个人出列。

他的下一个人又从1开始报数,数到m的那个人又出列。

依此规则重复下去,直到所有人全部出列。

求解最后一个出列的人的编号。

本次实验是以顺序表求解约瑟夫环问题,程序流程图及程序运行结果如下:输入人数、所报数、第一个报数人编号存储并建立一个约瑟夫环通过循环结构依次查找每次出列的人的编号并输出输出最后一个出列的人的编号程序代码如下:#include<iostream>#include<process.h>#include<stdlib.h>using namespace std;struct Node //循环节点的定义{int number; //编号Node *next;};Node *CreateList(Node *L,int &n,int &m); //建立约瑟夫环函数void Joseph(Node *L,int n,int m); //输出每次出列号数函数Node *DeleteList(Node **L,int i,Node *q); //寻找每次出列人的号数int LengthList(Node *L); //计算环上所有人数函数void main() //主函数{system("color 75"); //设置颜色以美观Node *L;L=NULL; //初始化尾指针int n, m;cout<<"请输入人数N:";cin>>n; //环的长度if(n<1){cout<<"请输入正整数!";} //人数异常处理else{cout<<"请输入所报数M:";cin>>m;if(m<1){cout<<"请输入正整数!";} //号数异常处理else{L=CreateList(L,n,m); //重新给尾指针赋值Joseph(L,n,m);}}system("pause");}Node *CreateList(Node *L,int &n,int &m) //建立一个约瑟夫环(尾插法){Node *q;for(int i=1;i<=n;i++){Node *p;p=new Node;p->number=i;p->next=NULL;if(i==1) L=q=p; //工作指针的初始化 else{q->next=p;q=q->next;}}q->next=L;if(L!=NULL){return(L);} //返回尾指针else cout<<"尾指针异常!"<<endl; //尾指针异常处理}void Joseph(Node *L,int n,int m) //输出每次出列的人{int k;cout<<"请输入第一个报数人:";cin>>k;if(k<1||k>n){cout<<"请输入1-"<<n<<"之间的数"<<endl;}else{cout<<"\n出列顺序:\n";for(int i=1;i<n;i++){Node *q = new Node;if(i==1) q=DeleteList(&L,k+m-1,q); //第一个出列人的号数else q=DeleteList(&L,m,q);cout<<"号数:"<<q->number<<endl;delete q; //释放出列人的存储空间}cout<<"最后一个出列号数是:"<<L->number<<endl; //输出最后出列人的号数}}Node *DeleteList(Node **L,int i,Node *q) //寻找每次出列的人{if(i==1) i+=LengthList(*L); //顺序依次出列情况的处理方式Node *p;p=*L;int j=0;while(j<i-2) {p=p->next;j++;}q = p->next;p->next=p->next->next;*L = p->next;return(q);}int LengthList(Node *L) //计算环上的人数{if(L){cout<<"尾指针错误!"<<endl;} //异常处理else{int i=1;Node *p=L->next;while(p!=L){i++;p=p->next;}return(i);}}实验体会:通过对本问题的分析,我进一步熟悉了对各种逻辑表达式的判断和指针的使用。

约瑟夫环问题(顺序结构)

约瑟夫环问题(顺序结构)

约瑟夫环问题(顺序结构)约瑟夫环问题(顺序结构)#define N 6int yuesefu1(int data[],int sum,int k){int i=0,j=0,count=0;while(count<sum-1)< p="">{if(data[i]!=0)/*当前人在圈子里*/j++;if(j==k)/*若该人应该退出圈子*/{ printf("\n%d",data[i]);data[i]=0;/*0表示不在圈子里*/count++;/*退出的人数加1*/j=0;/*重新数数*/}i++;/*判断下一个人*/if(i==sum)/*围成一圈*/i=0;}for(i=0;i<sum;i++)< p="">if(data[i]!=0)return data[i];/*返回最后一个人的编号*/}void main(){int data[N];int i,j,total,k;printf("\nPlease input the number of every people.\n");for(i=0;i<="" p="">{int input;scanf("%d",&input);if(input==0)break;/*0表示输入结束*/for(j=0;j<="" p="">if(data[j]==input)break;if(j>=i&&input>0)/*无重复,记录编号,继续输入*/{data[i]=input;i++;}elseprintf("\nData error.Re-input:");}total=i;printf("\nYou have input:\n");for(i=0;i<total;i++)< p="">{if(i%10==0)printf("\n");printf("%4d",data[i]);}printf("\nPlease input a number to count:");scanf("%d",&k);printf("\nThe last one''''s number is %d",yuesefu1(data,total,k)); }</total;i++)<></sum;i++)<> </sum-1)<>。

约瑟夫环(顺序表)

约瑟夫环(顺序表)

test:下标值。本次报数完之后,下一个 值为本单元中存放的值,即下一个 test =
P[test]
#include <stdio.h>// main(){
int i, p[17],test, head; // 设置循环控制变量、数组及两个数组下标指针 for(i=0;i<16;i++
p[i]=i+1; //存放下一个单元的下标值(位置号) p[16]=0; test=0; //起始位置(从哪开始报数) while(test!=p[test]){ // 位置号和该位置的下一位置相同时退出
(位置号) i + 1,即 P[ i ] = i + 1;到最后一个人的时候就循 环到第一个人,设置P[16] =0。
3
P[i] 1 2 3 4 5 6 7 8
0
i
01 2 34 5 6 7
16

head:下标值。由于报到3的倍数的 人要退出,为了保持循环顺序表的形 式,要改变退出的人的前一个的值, 例如:要改变P[1]的值使之变为3。 head用来标识报数的前一个人的下标 值,当报到3的倍数的人退出时,改 变前一个人的单元值为报到3的倍数 的人的单元值,使之保持循环顺序表 的形式。如此下去,当只有一个人的 时候test=p[test]。
指针指向的下一位置
test=p[head]; //记住出列位置
} printf(″\n%5d″,test); // 最后一个出列位置
}
❖ 例:设计一个程序求约瑟夫环的出列顺序。约瑟夫 (Joseph)问题:有17个人按顺时针方向围坐一周 (编号为0~16),从第0号的人开始从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开始计数,直到最后一个结点从链表中删除算法结束。

约瑟夫问题详解(CC++)

约瑟夫问题详解(CC++)

约瑟夫问题详解(CC++)Josephus 约瑟夫问题假设n个竞赛者排成一个环形,依次顺序编号1,2,…,n。

从某个指定的第1号开始,沿环计数,每数到第m个人就让其出列,且从下一个人开始重新计数,继续进行下去。

这个过程一直进行到所有的人都出列为止。

最后出列者为优胜者。

无论是用链表实现还是用数组实现来解约瑟夫问题都有一个共同点:要模拟整个游戏过程,不仅程序写起来比较麻烦,而且时间复杂度高达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 --> 0k+1 --> 1k+2 --> 2......k-2 --> n-2变换后就完完全全成为了(n-1)个人报数的子问题,假如我们知道这个子问题的解:例如x 是最终的胜利者,那么根据上面这个表把这个x变回去不刚好就是n个人情况的解吗?变回去的公式很简单:x'=(x+k)%n如何知道(n-1)个人报数的问题的解?显然,只要知道(n-2)个人的解就行了。

(n-2)个人的解呢?当然是先求(n-3)的情况---- 这显然就是一个倒推问题!递推公式:令f[i]表示i个人玩游戏报m退出最后胜利者的编号,最后的结果自然是f[n]递推公式f[1]=0;f[i]=(f[i-1]+m)%i; (i>1)有了这个公式,我们要做的就是从1-n顺序算出f[i]的数值,最后结果是f[n]。

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

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

约瑟夫环问题的两种解法(详解)约瑟夫环问题的两种解法(详解)题⽬: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。

约瑟夫环问题及注释

约瑟夫环问题及注释

约瑟夫环问题及程序问题描述:编号是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位幸存者。

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

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

约瑟夫环问题(最简单的数学解法)基本问题描述:已知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即可。

Josephu(约瑟夫)问题解析

Josephu(约瑟夫)问题解析

Josephu(约瑟夫)问题解析Josephu问题为:设置编号为1,2,3,......n的n个⼈围坐⼀圈,约定编号为k(1<=k<=n)的⼈从1看是报数,数到m的那个⼈出列,它的下⼀位⼜从1开始报数,数到m的那个⼈出列,以此类推,直到所有⼈出列为⽌,由此产⽣⼀个出队编号的序列。

提⽰:⽤有个不带头的循环链表来处理Josephu问题:先构成⼀个有n个结点的单循环链表,然后由k结点起从1开始计数,计到m时,对应结点从链表中删除,然后再从被删除结点的下⼀个结点⼜从1开始计数,直到最后⼀个结点从链表中删除算法结束。

代码:public class Demo{public static void main(String[] args){CycLink cyclink=new CycLink();cyclink.setLen(5);cycLink.createLink();cyclink.show();}}//⼩孩class Child{//编号int no;//结点Child nextChild=null;public Child(int no){//给编号this.no=no;}}//环形链表class CycLink{//先定义⼀个指向链表第⼀个⼩孩的引⽤//指定第⼀个⼩孩的引⽤不能动,不然以后找不到他了Child firstChild=null;//定义⼀个游标Child temp=null;//表⽰共有⼏个⼩孩int len=0;//设置链表⼤⼩public void setLen(int len){this.len=len;}//初始化环形链表public void createLink(){for(int i=1;i<=len;i++){if(i==1){//创建第⼀个⼩孩Child ch=new Child(i);this.firstChild=ch;this.temp=ch;}else{//创建最后⼀个⼩孩if(i==len){Child ch=new Child(i);temp.nextChild=ch;temp=ch;temp.nextChild=this.firstChild;}else{//继续创建⼩孩Child ch=new Child(i);//连接,搭桥temp.nextChild=ch;//temp向前⾛⼀步,指向刚刚进来的孩⼦temp=ch;}}}}//打印该环形链表public void show(){Child temp=this.firstChild;do{System.out.println(temp.no);temp=temp.nextChild;}while(temp!=this.fistChild);}}优化:代码:public class Demo{public static void main(String[] args){CycLink cyclink=new CycLink();cyclink.setLen(50);cycLink.createLink();cycLink.setK(2);cycLink.setM(3);cyclink.show();cyclink.play();}}//⼩孩class Child{//编号int no;//结点Child nextChild=null;public Child(int no){//给编号this.no=no;}}//环形链表class CycLink{//先定义⼀个指向链表第⼀个⼩孩的引⽤//指定第⼀个⼩孩的引⽤不能动,不然以后找不到他了 Child firstChild=null;//定义⼀个游标Child temp=null;//表⽰共有⼏个⼩孩int len=0;int k=0;int m=0;//设置mpublic void setM(int m){this.m=m;}//设置链表⼤⼩public void setLen(int len){this.len=len;}//设置从第⼏个⼈开始数数public void setK(int k){this.k=k;}//开始playpublic void play(){Child temp=this.fistChild;//1.先找到开始数数的⼈//int i=1;i<k;因为⾃⼰也要数⼀下,所以i不能为k for(int i=1;i<k;i++){temp=temp.nexChild;}while(this.len!=1){//2.数m下for(int j=1;j<m;j++){temp=temp.nextChild;}//找到要出圈的前⼀个⼩孩,有待优化Child temp2=temp;while(temp2.nextChild!=temp){temp2=temp2.nextChild;}//3.将数到m的⼩孩,退出圈temp2.nextChild=temp.nextChild;//让temp指向数数的⼩孩temp=temp.nextChild;this.len--;}//最后⼀个⼩孩(验证)System.out.println(temp.no);}//初始化环形链表public void createLink(){for(int i=1;i<=len;i++){if(i==1){//创建第⼀个⼩孩Child ch=new Child(i);this.firstChild=ch;this.temp=ch;}else{//创建最后⼀个⼩孩if(i==len){Child ch=new Child(i);temp.nextChild=ch;temp=ch;temp.nextChild=this.firstChild;}else{//继续创建⼩孩Child ch=new Child(i);//连接,搭桥temp.nextChild=ch;//temp向前⾛⼀步,指向刚刚进来的孩⼦ temp=ch;}}}}//打印该环形链表public void show(){Child temp=this.firstChild;do{System.out.println(temp.no);temp=temp.nextChild;}while(temp!=this.fistChild);}}。

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,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;}。

约瑟夫环问题(Josephus)

约瑟夫环问题(Josephus)
输入格式:
第一行为一个整数T(<2^15)表示测 试次数,接着第二到T+1行分别为n,m和k 的值。 例:2
10 2 3
输出格式:
T行最后min(n,3)个出列的编号。 结果:6 1 5
问题背景
• 这个问题是以弗拉维奥•约瑟夫斯命名的, 它是1世纪的一名犹太历史学家。他在自己 的日记中写道,他和他的40个战友被罗马 军队包围在洞中。他们讨论是自杀还是被 俘,最终决定自杀,并以抽签的方式决定 谁杀掉谁。约瑟夫斯和另外一个人是最后 两个留下的人。约瑟夫斯说服了那个人, 他们将向罗马军队投降,不再自杀。
(3)us 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]+" ");
68 34 25 38 54 4 120 16 23 32 53 97
500 12 30 166 358 266
实验总结:
(1) 经过这次的实践,让我们明白了合作 的重要性。
(2)在程序设计初期,总会或多或少的出现 问题,经过我们的耐心调试,不断地修改, 慢慢地将程序设计较好的符合了要求。
(3)当然其中还是会存在一些漏洞,需要进 一步的改进。在计算机中是容不得丝毫的 错误的,这也让我们学到了面对科学要持 有严谨的态度,否则必定得不到应该有的 结果。

实习报告一:用顺序表实现约瑟夫环

实习报告一:用顺序表实现约瑟夫环

实习报告一:用顺序表实现约瑟夫环题目:用顺序表和循环链表实现约瑟夫环的出列顺序。

班级:06计算机B班姓名:学号:0621121005 完成日期:2008-6-14顺序表实现一、需求分析1.本程序中,需要输入的内容均为整型数,输入完成后以“回车符”作为结束标志。

2.程序以用户和计算机的对话方式执行,即在计算机终端上显示“提示信息”之后,由用户在键盘上输入程序的相关数据。

3.程序执行的命令包括:(1)请输入原始密码m和人数n(n不大于30);(2)请依次输入每个人的密码;(3)屏幕上将依次显示出列者的编号;(4)结束。

4.测试数据:m的初值为20;n=7,7个人的密码依次为:3,1,7,2,4,8,4。

实验结果应该为:6,1,4,7,2,3,5。

二、概要设计要用顺序链表实现上述的功能,需要一个抽象数据类型:顺序表。

1.顺序表数据类型定义:ADT sqlist {数据对象:D={ a i |a i∈elemset,i=1,2,…,n, n≥0}数据关系:R1={ <a i-1,a i> | a i-1,a i ∈D,i=2,3,…,n}基本操作:initlist_sq(&l)操作结果:构造一个空的顺序表l。

}ADT sqlist2.本程序包含一个模块:主程序模块:顺序表抽象数据类型;Int main(){接受命令;处理命令;}三、详细设计1.源程序#include<iostream>using namespace std;#define maxlen 30typedef int elemtype;typedef struct{elemtype elem[maxlen];int length;}sqlist;sqlist joseph;void initlist_sq(sqlist &l){l.length=0;}void jos(int m, int n){cout<<"请依次输入"<<n<<"个密码:";for(;joseph.length<n;joseph.length++)cin>>joseph.elem[joseph.length];//赋密码值cout<<"出列顺序为:";int i=-1;//i为数组下标(下一值为0)for(int k=0;k<n;k++){for(int j=0;j<m;){i=(i+1)%n;if(joseph.elem[i]!=0)j++;}cout<<i+1<<" ";m=joseph.elem[i];joseph.elem[i]=0;}cout<<endl;}void main(){int m,n;//m为密码,n为小孩个数cout<<"请输入初始密码m和人数n(n不大于30):";do{cin>>m>>n;if(n<1||n>30) cout<<"人数不合法,请重输(n不大于30):"<<endl;}while(n<1||n>30);initlist_sq(joseph);// 初始化线性表jos(m,n);}2.函数调用关系:main→jos。

计算机语言设计约瑟夫环数学应用题

计算机语言设计约瑟夫环数学应用题

计算机语言设计约瑟夫环数学应用题一、问题描述约瑟夫(Joseph)问题的一种描述是:编号为1,2,…,n的n个人按顺时针方向围坐一圈,每人持有一个密码(正整数)。

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

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

试设计一个程序求出出列顺序。

二、基本要求利用利用顺序表存储结构及单向循环链表存储结构模拟约瑟夫环,按照出列的顺序印出各人的编号三、测试数据m的上限为20,初值为6;(1) 对于n=7,7个人的密码依次为:3,1,7,2,4,8,4进行测试。

(2) 对于从键盘输入的n和n个人的密码进行测试。

四、用数组实现约瑟夫环程序代码:#include<stdio.h>void main(){int num[100];int m,n,i,k=0,s=0;int *p=num;printf("输入初始密码和人数n \n");scanf("%d%d",&m,&n);printf("个人密码\n");for(i=1;i<=n;i++)scanf("%d",&num[i]);i=1;while(s<=(n-1)) //当未退出人数(s)大于等于一时执行循环体{if(*(p+i)!=0)k++;if(k==m){printf("%d ",i);m=*(p+i);*(p+i)=0; // 对退出的人编号置零k=0;s++;}i++;if(i==(n+1)) //当循环到最后一位时返回到第一位i=1;}}测试数据结果:五.用链表实现约瑟夫环程序代码:#include<stdio.h>#include<malloc.h>typedef struct list{int num;int passward;struct list *next;}node;node *creatlist(int n){node *p,*q,*head;int i=1;head=(node *)malloc(sizeof(node));head->num=i;printf("请输入第1个密码:");scanf("%d",&head->passward);p=head;for(i=2;i<=n;i++){q=(node *)malloc(sizeof(node));if(q==0)return 0;printf("请输入第%d个人的密码:",i);scanf("%d",&q->passward);q->num=i;p->next=q;p=q;}p->next=head;return head;}void josephus(node *l,int k){int i;node *p=l,*q,*s;while(p->next!=p){for(i=1;i<k;i++){q=p;p=p->next;}printf("%d ",p->num);k=p->passward;s=p;q->next=p->next;p=p->next;free(s);}printf("%d",p->num);printf("\n");}void main(){node *l;int n,k;printf("请输入实验人数:");scanf("%d",&n);printf("请输入初始key的值:");scanf("%d",&k);l=creatlist(n);josephus(l,k);}测试数据结果:六:学习体会和实验中遇到的问题约瑟夫环,经过理解,可以认为是一个数学应用题:已知n个人围坐在一张圆桌周围。

约瑟夫问题(循环链表)

约瑟夫问题(循环链表)

约瑟夫问题(循环链表)问题描述:约瑟夫是犹太军队的⼀个将军,在反抗罗马的起义中,他所率领的军队被击溃,只剩下残余的部队40余⼈,他们都是宁死不屈的⼈,所以不愿投降做叛徒。

⼀群⼈表决说要死,所以⽤⼀种策略来先后杀死所有⼈。

于是约瑟夫建议:每次由其他两⼈⼀起杀死⼀个⼈,⽽被杀的⼈的先后顺序是由抽签决定的,约瑟夫有预谋地抽到了最后⼀签,在杀了除了他和剩余那个⼈之外的最后⼀⼈,他劝服了另外⼀个没死的⼈投降了罗马。

1//利⽤循环链表模拟约瑟夫问题,把41个⼈⾃杀的顺序编号输出2 #include<iostream>3 #include<malloc.h>4 #include<string.h>5 #include<stdlib.h>6 #include<stdio.h>7using namespace std;89 typedef struct Node{10int data;11struct Node *next;12 }Node,*LinkList;1314void InitList(LinkList &L){15 L = (LinkList)malloc(sizeof(Node));16if(!L)17 exit(0);18 L->next = NULL;19 }2021 LinkList CreatList(LinkList &L, int n){//尾插法创建单链表22 LinkList r,p;23 r = L;24for(int i = 1; i <= n; i++){25 p = (LinkList)malloc(sizeof(Node));26 p->data = i;27 r->next = p;28 r = p;29 }30 r->next = NULL;31 p->next = L->next;//尾部指向头部,循环链表32return p->next;//返回头结点的头指针(构成循环)33 }3435int main(){36int n = 41;37int m = 3;38 LinkList L,p;39 InitList(L);40 p = CreatList(L,n);41 LinkList temp;42 m %= n;43while(p != p->next){44for(int i = 1;i < m-1; i++){45 p = p->next;46 }47 printf("%d->",p->next->data);48//删除结点49//p->next->next;50 temp = p->next;51 p->next = temp->next;52free(temp);53 p = p->next;54 }55 cout<<p->data;56return0;57 }运⾏结果:关于约瑟夫问题的题⽬:n个⼈围成⼀个圈,每个⼈分别标注为1、2、...、n,要求从1号从1开始报数,报到k的⼈出圈,接着下⼀个⼈⼜从1开始报数,如此循环,直到只剩最后⼀个⼈时,该⼈即为胜利者。

约瑟夫环问题详解

约瑟夫环问题详解

约瑟夫环问题详解很久以前,有个叫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]输出,然后将后⾯的值往前移动⼀位,直到所有的节点出列。

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

计算机科学与工程学院
《算法与数据结构》试验报告[一]
专业班级
试验地点 学生学号 指导教师 学生姓名
试验时间
试验项目 算法与数据结构
试验类别
基础性() 设计性() 综合性(√) 其它( ) 试验目的及要求
(1)掌握用VC++上机调试线性表的基本方法; (2)掌握顺序表的存储结构以及基本运算的实现。

成 绩 评 定 表
类 别 评 分 标 准 分值 得分 合 计
上机表现
积极出勤、遵守纪律 主动完成设计任务 30分
程序与报告
程序代码规范、功能正确 报告详实完整、体现收获
70分
printf("您想出列的序号为:");
scanf("%d",&tt);
josephus(L,tt);
}
四、测试用例(尽量覆盖所有分支)
1.当n>m且n%m!=0时,即当n不是m的倍数时:
n=12,m=5时约瑟夫环的实验结果为
2.当n>m且n%m=0时,即当n是m的倍数时:
n=15,m=5时约瑟夫环的实验结果为
3. 当n<m且n%m=0时,即当n是m的约数时:
n=4,m=12时约瑟夫环的实验结果为
4.当n<m且n%m!=0时,即当n不是m的约数时:
n=3,m=7时约瑟夫环的实验结果为
5.当n或m任一个足够大时
n=150,m=7时约瑟夫环的实验结果为
五、实验总结。

相关文档
最新文档