用双向循环链表求解约瑟夫环
C语言用循环单链表实现约瑟夫环
C语⾔⽤循环单链表实现约瑟夫环⽤循环单链表实现约瑟夫环(c语⾔),供⼤家参考,具体内容如下源代码如下,采⽤Dev编译通过,成功运⾏,默认数到三出局。
主函数:main.c⽂件#include <stdio.h>#include "head.h"#include "1.h"int main(){Linklist L;int n;printf("请输⼊约瑟夫环中的⼈数:");scanf("%d",&n);Createlist(L,n);printf("创建的约瑟夫环为:\n");Listtrave(L,n);printf("依次出局的结果为:\n");Solution(L,n);return 0;}head.h⽂件:#include "1.h"#include <stdio.h>#include <stdlib.h>typedef int Elemtype;typedef struct LNode{Elemtype data;struct LNode *next;}LNode,*Linklist;void Createlist(Linklist &L,int n){Linklist p,tail;L = (Linklist)malloc(sizeof(LNode));L->next = L;//先使其循环p = L;p->data = 1;//创建⾸节点之后就先给⾸节点赋值,使得后⾯节点赋值的操作能够循环tail = L;for(int i = 2;i <= n;i++){p = (Linklist)malloc(sizeof(LNode));p->data = i;p->next = L;tail->next = p;tail = p;}printf("已⽣成⼀个长度为%d的约瑟夫环!\n",n);}void Listtrave(Linklist L,int n)//遍历函数{Linklist p;p = L;for(int i = 1;i <= n;i++){printf("%3d",p->data);p = p->next;}printf("\n");}int Solution(Linklist L,int n){Linklist p,s;p = L,s = L;int count = 1;while(L){if(count != 3){count++;p = p->next;//进⾏不等于3时的移位}else{Linklist q;q = p;//⽤q保存p所指的位置,⽅便进⾏节点的删除if(s->next->data == s->data)//当只有⼀个元素的时候{printf("%3d\n",s->data);free(s);return OK;}else//当有两个及两个以上的元素的时候{count = 1;//先将count重置为1printf("%3d",p->data);//再打印出出局的值while(s->next != p){s = s->next;//将s移位到p的前驱节点处}p = p->next;//使p指向⾃⼰的下⼀个节点s->next = p;//进⾏删除free(q);}}}}1.h⽂件:#define TRUE 1#define FALSE 0#define OK 1#define ERROR 0#define INFEASIBLE -1#define OVERFLOW -2运⾏结果:以上就是本⽂的全部内容,希望对⼤家的学习有所帮助,也希望⼤家多多⽀持。
约瑟夫环问题实验报告
约瑟夫问题实验报告背景约瑟夫问题(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个数输出到屏幕上三.详细设计首先,设计实现约瑟夫环问题的存储结构。
实验报告 约瑟夫问题
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;
}
实验中遇到的问题及解决方法
实验结果如下:
实验总结(结果和心得体会)
C++编写的 约瑟夫环问题 代码
程序源代码:#include <stdio.h>#include <malloc.h>#include<conio.h>#include <stdlib.h>#include<ctime>#define NULL 0typedef struct Node{int m;//密码int n;//序号struct Node *next;}Node,*Linklist;Linklist create(int z) //生成循环单链表并返回,z为总人数{int i,mm;Linklist H,r,s;H=NULL;printf("请按顺序依次为每个人添加密码:");for(i=1;i<=z;i++){printf("\ninput cipher=");scanf("%d",&mm);s=(Linklist)malloc(sizeof(Node));s->n=i;s->m=mm;printf("%d号的密码%d",i,s->m);if(H==NULL)//从链表的第一个节点插入{H=s;r=H;}else//链表的其余节点插入{r->next=s;r=s;//r后移}//for结束r->next=H;/*生成循环单链表*/return H;}void search(Linklist H,int m0,int z)//用循环链表实现报数问题{int count=1;//count为累计报数人数计数器int num=0;//num为标记出列人数计数器Linklist pre,p;p=H;printf("出列的顺序为:");while(num<z){do{count++;pre=p;p=p->next;}while(count<m0);{pre->next=p->next;printf("%d ",p->n);m0=p->m;free(p);p=pre->next;count=1;num++;}//while结束}void clean(){int system(const char *string);int inquiry;printf("请问需要清除上一次操作记录吗(1.清屏/2.不清屏)...?\n"); scanf("%d",&inquiry);if(inquiry ==1)system("cls");}void text(){int m0,z,i, choose,k=1; //k用来阻止第一次进入程序清屏操作Linklist H;bool chooseFlag=false;while(1){if(k!=1)clean();k++;while(!chooseFlag){printf(" ……………………欢迎进入约瑟夫环问题系统…………………… \n"); printf( "* 1.输入约瑟夫环数据 * \n"); printf(" * 2.什么是约瑟夫环 * \n"); printf(" * 3.退出系统 * \n"); printf("........................................................ \n"); printf("请输入相应的数字进行选择: ");scanf("%d",&choose);for(i=1;i<=4;i++){if(choose==i) { chooseFlag=true; break;}else chooseFlag=false;}if(!chooseFlag) printf("Error Input!\n");} //end while(!chooseFlag)if(choose==1) //if 开始{printf("Input how many people in it:");//z为总人数scanf("%d",&z);if(z<=30){H=create(z);//函数调用printf("\nInput the start code m0=");scanf("%d",&m0);search(H,m0,z);printf("\n\n\n");}else{printf("超过最大输入人数\n");break;}}else if(choose==2){printf("\n约瑟夫环问题:设有n个人,其编号分别为1,2,3,…,n,安装编号顺序顺时针围坐一圈。
循环双链表特点
循环双链表特点循环双链表是一种特殊的数据结构,它具有循环和双向链表的特点。
循环双链表中的每个节点都包含两个指针,一个指向前一个节点,一个指向后一个节点。
最后一个节点的后指针指向头节点,头节点的前指针指向最后一个节点,从而形成了一个闭环。
循环双链表的特点如下:1. 双向性:每个节点都有两个指针,分别指向前一个节点和后一个节点。
这样可以方便地在任意位置插入或删除节点,而不需要像单链表那样需要遍历找到前驱节点。
2. 循环性:循环双链表是一个闭环,即最后一个节点的后指针指向头节点,头节点的前指针指向最后一个节点。
这样可以方便地进行循环遍历,不需要判断是否到达了链表的末尾。
3. 动态性:循环双链表可以动态地增加或删除节点,而不需要预先指定链表的长度。
4. 灵活性:循环双链表可以在任意位置插入或删除节点,不受限于只能在链表的头部或尾部进行操作。
这样可以方便地实现栈、队列等数据结构。
5. 代码实现简单:相比于其他数据结构,循环双链表的代码实现相对简单,只需要处理好节点之间的指针关系即可。
循环双链表的应用领域非常广泛,特别是在需要频繁插入和删除节点的场景中,循环双链表能够提供高效的插入和删除操作。
下面以几个具体的应用场景来展开对循环双链表的解释和扩展。
1. 缓存替换算法:循环双链表可以用于实现LRU(Least Recently Used)缓存替换算法。
LRU算法中,当缓存满时,需要替换掉最近最少使用的数据。
循环双链表可以维护数据的访问顺序,每次访问一个数据时,将其移到链表的头部;当缓存满时,删除链表尾部的数据即可。
这样就可以保证链表头部的数据是最近访问的数据,尾部的数据是最久未访问的数据。
2. 轮播图:循环双链表可以用于实现轮播图功能。
轮播图需要循环展示多张图片,循环双链表正好可以满足这个需求。
每个节点表示一张图片,节点之间通过指针连接起来形成一个循环链表。
通过不断地遍历链表,可以实现图片的自动切换。
3. 约瑟夫环问题:循环双链表可以用于解决约瑟夫环问题。
约瑟夫环问题
约瑟夫环问题问题描述:有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++数据结构之约瑟夫环
2009级数据结构实验报告实验名称:实验线性表实现约瑟夫问题求解学生姓名:桂柯易班级:2009211120班内序号:07学号:09210580日期:2010年10月31日1.实验要求【实验目的】1.熟悉C++语言的基本编程方法,掌握集成编译环境的调试方法;2.学习指针、模板类、异常处理的使用;3.掌握线性表的操作实现方法;4.培养使用线性表解决实际问题的能力。
【实验内容】利用循环链表实现约瑟夫问题的求解。
约瑟夫问题如下:已知n个人(n>=1)围坐一圆桌周围,从1开始顺序编号。
从序号为1的人开始报数,顺时针数到m的那个人出列。
他的下一个人又从1开始报数,数到m 的那个人又出列。
依此规则重复下去,直到所有人全部出列。
请问最后一个出列的人的编号。
2.程序分析2.1 存储结构存储结构:循环链表2.2 关键算法分析【设计思想】首先,设计实现约瑟夫环问题的存储结构。
由于约瑟夫环本身具有循环性质,考虑采用循环链表,为了统一对表中任意节点的操作,循环链表不带头结点。
循环链表的结点定义为如下结构类型:struct Node{int number;Node *next;};其次,建立一个不带头结点的循环链表并由头指针first指示。
最后,设计约瑟夫环问题的算法。
【伪代码】1、工作指针first,r,s,p,q初始化2、输入人数(n)和报数(m)3、循环n次,用尾插法创建链表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);}4、输入报数的起始人号数k;5、Node *q = new Node;计数器初始化i=1;6、循环n次删除结点并报出位置(其中第一个人后移k个)当i<n时移动指针m-2次p=p->next;删除p结点的后一结点qq=p->next;p->next=q->next;*L = p->next;报出位置后Delete q;计数器i++;【复杂度】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;}时间复杂度:O(n)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);时间复杂度:O(n2)算法的空间复杂度:O(n2)2.3 其他程序源代码:#include<iostream>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()//主函数{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);}}3.程序运行结果1.测试主函数流程:2.测试条件:如上图所示,人数为20人,所报数为6,第一个报数的人是1号。
约瑟夫环数据结构实验报告
约瑟夫环数据结构实验报告约瑟夫环数据结构实验报告引言约瑟夫环是一种经典的数学问题,它涉及到一个有趣的数据结构。
本次实验旨在通过实现约瑟夫环数据结构,深入理解该问题,并探索其在实际应用中的潜力。
本报告将介绍实验的设计和实现过程,并分析实验结果。
实验设计在本次实验中,我们选择使用链表来实现约瑟夫环数据结构。
链表是一种非常灵活的数据结构,适合用于解决约瑟夫环问题。
我们设计了一个Josephus类,其中包含了创建环、添加元素、删除元素等操作。
实验实现1. 创建环在Josephus类中,我们首先需要创建一个循环链表。
我们使用一个头节点来表示环的起始位置。
在创建环的过程中,我们可以选择指定环的长度和起始位置。
2. 添加元素在创建环之后,我们可以通过添加元素来向约瑟夫环中插入数据。
我们可以选择在环的任意位置插入元素,并且可以动态地调整环的长度。
3. 删除元素根据约瑟夫环的规则,每次删除一个元素后,下一个元素将成为新的起始位置。
我们可以通过删除元素的操作来模拟约瑟夫环的运行过程。
在删除元素时,我们需要考虑环的长度和当前位置。
实验结果通过实验,我们得出了以下结论:1. 约瑟夫环数据结构可以有效地模拟约瑟夫环问题。
通过创建环、添加元素和删除元素的操作,我们可以模拟出约瑟夫环的运行过程,并得到最后剩下的元素。
2. 约瑟夫环数据结构具有一定的应用潜力。
除了解决约瑟夫环问题,该数据结构还可以用于其他类似的问题,如任务调度、进程管理等。
3. 约瑟夫环数据结构的时间复杂度较低。
由于约瑟夫环的特殊性质,我们可以通过简单的链表操作来实现该数据结构,使得其时间复杂度较低。
结论本次实验通过实现约瑟夫环数据结构,深入理解了该问题,并探索了其在实际应用中的潜力。
通过创建环、添加元素和删除元素的操作,我们可以模拟出约瑟夫环的运行过程,并得到最后剩下的元素。
约瑟夫环数据结构具有一定的应用潜力,并且具有较低的时间复杂度。
通过本次实验,我们对数据结构的设计和实现有了更深入的理解,并为将来的研究和应用奠定了基础。
约瑟夫问题大全
“约瑟夫”问题及若干变种林厚从例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个人按顺时针方向围坐一圈,每人手持一个密码(正整数)。
一开始任选一个整数作为报数上限值,从第一人开始顺时针自 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。
C语言的循环链表和约瑟夫环
C语言的循环链表和约瑟夫环C语言的循环链表和约瑟夫环约瑟夫问题)是一个数学的应用问题,对于学习C语言四非常挺有帮助的,下面是店铺为大家搜集整理出来的有关于C语言的循环链表和约瑟夫环,一起了解下吧!循环链表的实现单链表只有向后结点,当单链表的尾链表不指向NULL,而是指向头结点时候,形成了一个环,成为单循环链表,简称循环链表。
当它是空表,向后结点就只想了自己,这也是它与单链表的主要差异,判断node->next是否等于head。
代码实现分为四部分:1. 初始化2. 插入3. 删除4. 定位寻找代码实现:1 2 3 4 5 6 7 8 9 1 0 1 1 1 2 1 3 1void ListInit(Node *pNode){int item;Node *temp,*target;cout<<"输入0完成初始化"<<endl; cin="">>item;if(!item)return ;if(!(pNode)){ //当空表的时候,head==NULLpNode = new Node ;if(!(pNode))exit(0);//未成功申请pNode->data = item;pNode->next = pNode;}else{//for(target = pNode;target->next!=pNode;target = target->next);4 15 16 17 18 19 2 0 2 1 2 2 2 3 2 4 2 5 2 6 2 7 2 8 2 9 3 0 3 1 3 2 3 3 3 4 3 5 3temp = new Node;if(!(temp))exit(0);temp->data = item;temp->next = pNode;target->next = temp;}}}void ListInsert(Node *pNode,int i){ //参数是首节点和插入位置Node *temp;Node *target;int item;cout<<"输入您要插入的值:"<<endl; cin="">>item;if(i==1){temp = new Node;if(!temp)exit(0);temp->data = item;for(target=pNode;target->next != pNode;target = target->next);temp->next = pNode;target->next = temp;pNode = temp;}else{target = pNode;for (int j=1;j<i-1;++j) target="target-">next;temp = new Node;if(!temp)exit(0);temp->data = item;temp->next = target->next;target->next = temp;}}void ListDelete(Node *pNode,int i){Node *target,*temp;if(i==1){for(target=pNode;target->next!=pNode;target=target ->next);temp = pNode;//保存一下要删除的首节点 ,一会便于释放6 37 38 39 4 0 4 1 4 2 4 3 4 4 4 5 4 6 4 7 4 8 4 9 5 0 5 1 5 2 5 3 5 4 5 5 5 6 5 7 5pNode = pNode->next;target->next = pNode;temp;}else{target = pNode;for(int j=1;j<i-1;++j) target="target-">next;temp = target->next;//要释放的nodetarget->next = target->next->next;temp;}}int ListSearch(Node *pNode,int elem){ //查询并返回结点所在的位置Node *target;int i=1;for(target = pNode;target->data!=elem && target->next!= pNode;++i)target = target->next;if(target->next == pNode && target->data!=elem)return 0;else return i;}</i-1;++j)></i-1;++j)></endl;></endl;>5 96 0 6 1 6 2 6 3 6 4 6 5 6 6 67 68 69 7 0 7 1 7 2 7 3 7 4 7 5 7 6 7 7 7 8 7 9 8约瑟夫问题约瑟夫环(约瑟夫问题)是一个数学的'应用问题:已知n个人(以编号1,2,3…n分别表示)围坐在一张圆桌周围。
约瑟夫环上机实验报告
约瑟夫环上机实验报告1. 概述约瑟夫环问题是一个经典的数学问题,该问题是以约瑟夫·弗拉维奥(Josephus Flavius)命名的,故称为约瑟夫环。
问题的具体描述如下:在编号为1到n的n 个人围成一个圆圈,从第一个人开始报数,报到m的人出列,然后从出列的下一个开始重新从1到m报数,再次报到m的人再次出列,如此循环下去,直到所有的人都出列为止。
本次实验旨在使用程序实现约瑟夫环的模拟,并观察对于不同的参数n和m,最后剩余的人的编号特点。
2. 实验设计2.1 算法设计本实验中采用循环链表来模拟约瑟夫环,首先构建一个含有n个结点的循环链表,每个结点表示一个人,每个结点的数据域存储该人的编号。
然后根据报数规则,依次遍历链表,当报数为m时,删除对应的结点。
直到链表中仅剩一个结点为止。
2.2 程序实现pythonclass ListNode:def __init__(self, val=0):self.val = valself.next = Nonedef josephus(n, m):if n == 0:return -1构建循环链表dummy = ListNode(-1)cur = dummyfor i in range(1, n + 1):node = ListNode(i)cur.next = nodecur = cur.nextcur.next = dummy.next模拟游戏过程count = 0while cur.next != cur:count += 1if count == m:cur.next = cur.next.nextcount = 0else:cur = cur.nextreturn cur.val3. 实验结果为了观察不同参数n和m对最后剩余的人的编号的影响,我们进行了多组实验。
结果如下:n m 最后剩余的人的编号5 2 310 3 415 4 1420 5 6从实验结果可以看出,最后剩余的人的编号与参数m有关,而与参数n无关。
约瑟夫环问题详解
约瑟夫环问题详解很久以前,有个叫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]输出,然后将后⾯的值往前移动⼀位,直到所有的节点出列。
数据结构实验报告约瑟夫环
数据结构实验报告约瑟夫环约瑟夫环是一个经典的问题,涉及到数据结构中的循环链表。
在本次数据结构实验中,我们将学习如何使用循环链表来解决约瑟夫环问题。
约瑟夫环问题最早出现在古代,传说中的犹太历史学家约瑟夫斯·弗拉维奥(Josephus Flavius)在围攻耶路撒冷时,为了避免被罗马人俘虏,与其他39名犹太人躲进一个洞穴中。
他们决定宁愿自杀,也不愿被敌人俘虏。
于是,他们排成一个圆圈,从第一个人开始,每次数到第七个人,就将他杀死。
最后剩下的人将获得自由。
在这个问题中,我们需要实现一个循环链表,其中每个节点表示一个人。
我们可以使用一个整数来表示每个人的编号。
首先,我们需要创建一个循环链表,并将所有人的编号依次添加到链表中。
接下来,我们需要使用一个循环来模拟每次数到第七个人的过程。
我们可以使用一个指针来指向当前节点,然后将指针移动到下一个节点,直到数到第七个人为止。
一旦数到第七个人,我们就将该节点从链表中删除,并记录下该节点的编号。
然后,我们继续从下一个节点开始数数,直到只剩下一个节点为止。
在实现这个算法时,我们可以使用一个循环链表的数据结构来表示约瑟夫环。
循环链表是一种特殊的链表,其中最后一个节点的指针指向第一个节点。
这样,我们就可以实现循环遍历链表的功能。
在实验中,我们可以使用C语言来实现循环链表和约瑟夫环算法。
首先,我们需要定义一个节点结构体,其中包含一个整数字段用于存储编号,以及一个指针字段用于指向下一个节点。
然后,我们可以实现创建链表、添加节点、删除节点等基本操作。
接下来,我们可以编写一个函数来实现约瑟夫环算法。
该函数接受两个参数,分别是参与游戏的人数和每次数到第几个人。
在函数内部,我们可以创建一个循环链表,并将所有人的编号添加到链表中。
然后,我们可以使用一个循环来模拟每次数到第几个人的过程,直到只剩下一个节点为止。
在每次数到第几个人时,我们可以删除该节点,并记录下其编号。
最后,我们可以返回最后剩下的节点的编号。
约瑟夫环问题的两种解法(详解)
约瑟夫环问题的两种解法(详解)约瑟夫环问题的两种解法(详解)题⽬: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,2,3...n分别表⽰)围坐在⼀张圆桌周围。
从编号为k的⼈开始报数,数到m的那个⼈出列;他的下⼀个⼈⼜从1开始报数,数到m的那个⼈⼜出列;依此规律重复下去,直到圆桌周围的⼈全部出列。
通常解决这类问题时我们把编号从0~n-1,最后结果+1即为原问题的解。
循环队列求解(链式)#include<stdio.h>#include<stdlib.h>//循环队列//typedef int ElemType;typedef struct QueueNode{int data;struct QueueNode *next;}QueueNode;typedef struct Queue{QueueNode *front;QueueNode *rear;}Queue;void InitQueue(Queue *q){q->front=q->rear=NULL;}void EnQueue(Queue *q , int value){QueueNode *temp=(QueueNode*)malloc(sizeof(QueueNode));temp->data=value;if(q->rear==NULL){temp->next=temp;q->rear=q->front=temp;}else{temp->next=q->rear->next;q->rear->next=temp;q->rear=temp;}}//enter a element from the tailvoid DeQueue(Queue *q, int *value){QueueNode *temp=(QueueNode*)malloc(sizeof(QueueNode)); if(q->rear==NULL){return;}// It's nullelse if(q->rear->next==q->rear){*value=q->front->data;free(q->rear);q->rear=q->front=NULL;}//It just has one nodeelse{*value=q->front->data;temp=q->front;q->front=temp->next;q->rear->next=q->front;}//more one nodefree(temp);}//delete a element from the headint main(){Queue *q=(Queue*)malloc(sizeof(Queue));int i,m,n,count,temp;printf("请输⼊⼈数n和循环要报的数m(两数之间留个空格)\n"); scanf("%d%d",&n,&m);for(i=1;i<=n;i++)EnQueue(q,i);printf("出圈序列:\n");while(q->front){ count=1;while(count<m){q->front=q->front->next;q->rear=q->rear->next;count++;}count=1;DeQueue(q,&temp);printf("%d ",temp);}putchar('\n');}简单解法#include <stdio.h>int josephus(int n, int m) {if(n == 1) {return0;}else {return (josephus(n-1, m) + m) % n;}}int main() {int n, m;while (scanf("%d", &n) == 1) {if (!n) {break;}scanf("%d", &m);int result = josephus(n, m);printf("%d\n", result+1);}return0;}。
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;}}。
约瑟夫环问题的两种解法(循环链表和公式法)
约瑟夫环问题的两种解法(循环链表和公式法)问题描述这⾥是数据结构课堂上的描述:N people form a circle, eliminate a person every k people, who is the final survior?Label each person with 0, 1, 2, ..., n - 1, denote(表⽰,指代) J(n, k) the labels of surviors when there are n people.(J(n, k)表⽰了当有 n 个⼈时幸存者的标号)First eliminate the person labeled k - 1, relabel the rest, starting with 0 for the one originally labeled k.0 1 2 3 ... k-2 k-1 k k+1 ... n-1... k-2 0 1 ...Dynamic programmingJ(n, k) = J(J(n - 1, k) + k) % n, if n > 1,J(1, k) = 0⽤中⽂的⽅式简单翻译⼀下就是 (吐槽:为啥课上不直接⽤中⽂呢?淦!) 有 n 个⼈围成⼀圈,从第⼀个⼈开始,从 1 开始报数,报 k 的⼈就将被杀死,然后从下⼀个⼈开始重新从 1 开始报数,往后还是报 k 的⼈被杀掉,杀到最后只剩⼀个⼈时,其⼈就为幸存者。
(上⾯的英⽂是从 0 开始的,是因为我们写程序时使⽤了数组,所以下标从 0 开始)解决⽅案循环链表⽅法算法思路很简单,我们这⾥使⽤了循环链表模拟了这个过程:节点 1 指向节点 2,节点 2 指向节点 3,...,然后节点 N 再指向节点 1,这样就形成了⼀个圆环。
如图所⽰,n 取 12,k 取 3,从 1 开始报数,然后依次删除 3, 6, 9, 12:#include<stdio.h>#include<stdlib.h>typedef struct Node // 节点存放⼀个数据和指向下⼀个节点的指针{int data;struct Node *next;} *NList; // NList为指向 Node 节点的指针// 创建⼀个节点数为 n 的循环链表NList createList(int n){// 先创建⼀个节点NList p, tmp, head;p = (NList)malloc(sizeof(struct Node));head = p; // 保存头节点p->data = 1; // 第⼀个节点for (int i = 2; i <=n ; i++){tmp = (NList)malloc(sizeof(struct Node));tmp->data = i;p->next = tmp;p = tmp;}p->next = head; // 最后⼀个节点指回开头return head;}// 从编号为 1 的⼈开始报数,报到 k 的⼈出列,被杀掉void processList(NList head, int k){if (!head) return;NList p = head;NList tmp;while (p->next != p){for (int i = 0; i < k - 1; i++){tmp = p;p = p->next;}printf("%d 号被杀死\n", p->data);tmp->next = p->next;free(p);p = NULL; // 防⽌产⽣野指针,下同p = tmp->next;}printf("幸存者为 %d 号", p->data);free(p);p = NULL;}int main(){NList head = createList(11);processList(head, 3);return 0;}测试结果:易知,这个算法的时间复杂度为O(nk),显然,这不是⼀个好的算法。
约瑟夫环设计实验报告
一、实验目的1. 理解并掌握约瑟夫环问题的基本原理和解决方法。
2. 熟悉循环链表在数据结构中的应用,并能够运用其解决实际问题。
3. 提高编程能力和算法设计能力,培养逻辑思维和问题解决能力。
二、实验内容1. 实验背景约瑟夫环问题是一个经典的数学问题,描述了N个人围成一圈,按照一定的规则进行报数,最终确定出列顺序的过程。
该问题在计算机科学、通信等领域有广泛的应用。
2. 实验原理本实验采用循环链表作为数据结构来模拟约瑟夫环问题。
循环链表是一种线性表,其特点是最后一个节点的指针指向第一个节点,形成一个环。
在本实验中,我们将每个节点表示为一个人,节点的数据域存储该人的编号。
3. 实验步骤1. 初始化循环链表:首先创建一个循环链表,包含N个节点,节点编号依次为1, 2, ..., N。
2. 设置报数上限:从键盘输入一个正整数M,作为报数上限。
3. 模拟报数过程:a. 从链表头节点开始,按照顺时针方向进行报数。
b. 当报数达到M时,将当前节点出列,并将M的值设置为该节点的数据域。
c. 将指针指向下一个节点,继续进行报数。
d. 重复步骤b和c,直到链表中只剩下一个节点。
4. 输出出列顺序:按照出列的顺序,将每个节点的编号打印出来。
4. 实验代码```c#include <stdio.h>#include <stdlib.h>typedef struct Node {int number;struct Node next;} Node;// 创建循环链表Node createList(int n) {Node head = NULL, tail = NULL, temp = NULL; for (int i = 1; i <= n; i++) {temp = (Node)malloc(sizeof(Node));temp->number = i;temp->next = NULL;if (head == NULL) {head = temp;tail = temp;} else {tail->next = temp;tail = temp;}}tail->next = head; // 形成循环链表return head;}// 打印出列顺序void printOrder(Node head) {Node temp = head;while (temp->next != temp) {printf("%d ", temp->number); temp = temp->next;}printf("%d\n", temp->number);}int main() {int n, m;printf("请输入人数: ");scanf("%d", &n);printf("请输入报数上限: ");scanf("%d", &m);Node head = createList(n);printOrder(head);// 释放内存Node temp;while (head->next != head) {temp = head;head = head->next;free(temp);}free(head);return 0;}```5. 实验结果与分析通过运行实验代码,可以得到约瑟夫环问题的出列顺序。
循环队列解决约瑟夫问题
循环队列解决约瑟夫问题有n个⼈围成⼀圈,从第1个⼈开始,1,2,…,m报数,报⾄m出局,余下的⼈继续从1,2,…,m报数,重复之前的流程,要求:求出被淘汰编号的序列,及最后剩下的⼀⼈是原来的第⼏号?(要求:⽤循环队列解决该问题。
)#ifndef STATUS_H#define STATUS_H#define TRUE 1#define FALSE 0#define OK 1#define ERROR 0#define INFEASIBLE -1#define OVERFLOW -2typedef int Status;#endif#include <iostream>using namespace std;#include "Status.h"typedef int ElemType;typedef struct{ElemType *base;int front;int rear;int MAXSIZE;}SqQueue;Status InitQueue(SqQueue& Q,int n) //初始化队列{Q.base = new ElemType[100];if(!Q.base){cout << "创建队列失败!";return ERROR;}Q.front=Q.rear=0;Q.MAXSIZE = n+1;//MAXSIZE是总⼈数+1,是为了留出⼀个空位置来放置rearreturn OK;}void QueueTraverse(SqQueue Q) //遍历队列{int i;i=Q.front;while(i!=Q.rear){cout<<Q.base[i]<<"";i=(i+1)%Q.MAXSIZE;}cout<<endl;}Status EnQueue(SqQueue& Q,ElemType e) //⼊队{if((Q.rear+1)%Q.MAXSIZE==Q.front){cout << "队列已满!";return ERROR;}Q.base[Q.rear] = e;Q.rear = (Q.rear+1)%Q.MAXSIZE;return OK;}Status DeQueue(SqQueue& Q,ElemType& e) //出队{if(Q.front==Q.rear){cout << "队列为空!";return ERROR;}e = Q.base[Q.front];Q.base[Q.front] = 0;Q.front = (Q.front+1)%(Q.MAXSIZE-1);//总⼈数为MAXSIZE-1return OK;}int main(){int n,m,i=1;SqQueue Q;ElemType e;cout << "请输⼊n个⼈(n<=100):";do{cin >> n;if(n>100 || n<1){cout << "输⼊⼈数错误!请重新输⼊:";}}while(n>100 || n<1);InitQueue(Q,n);while(i<=n)//⼊队操作{EnQueue(Q,i);i++;}cout << "\n此时的序列顺序为:"<<endl;QueueTraverse(Q);cout << "\n请输⼊第m个⼈出队(1<=m<=n):";do{cin >> m;if(m>n || m<1){cout << "m输⼊错误!请重新输⼊:";}}while(m>n || m<1);cout << endl;int Count = n;//⽤来记录剩下的⼈数while(Count != 1){i = 1;//i⽤来控制是第⼏个⼈报数while(i != m)//当i的值不等于m的值时{Q.front = (Q.front+1)%(Q.MAXSIZE-1);//总⼈数为MAXSIZE-1if(Q.base[Q.front] != 0)//当此时不为0的话,i++⽤来控制第⼏个⼈{i++;}}DeQueue(Q,e);while(Q.base[Q.front] == 0)//当此时为0的时候,循环找到下⼀个不为0的位置{Q.front = (Q.front+1)%(Q.MAXSIZE-1);}cout << "序号:" << e << "出局!\n";Count--;}DeQueue(Q,e);cout << "\n最后⼀个是:" << e << endl;return0;}1.有n个⼈围成⼀圈,从第1个⼈开始,1,2,…,m报数,报⾄m出局,余下的⼈继续从1,2,…,m报数,重复之前的流程,要求:求出被淘汰编号的序列,及最后剩下的⼀⼈是原来的第⼏号?(要求:⽤循环队列解决该问题。
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
用双向循环链表求解约瑟夫环学校:东北大学专业:计算机科学与技术1.问题描述Josephus排列问题定义如下:假设n个竞赛者排成一个环形。
给定一个正整数m≤n,从第1人开始,沿环计数,第m人出列。
这个过程一直进行到所有人都出列为止。
最后出列者为优胜者。
全部出列次序定义了1,2,…n的一个排列。
称为(n,m)Josephus排列。
例如,(7,3)Josephus排列为3,6,2,7,5,1,4。
【实验要求】设计求解Josephus排列问题程序。
(1)采用顺序表、单链表或双向循环链表等数据结构。
(2)采用双向循环链表实现Josephus排列问题,且奇数次顺时针轮转,偶数次逆时针轮转。
(3)推荐采用静态链表实现Josephus排列问题。
2.需求分析本程序要求根据输入的人数n和给定的正整数m,求得约瑟夫排列,且奇数次顺时针轮转,偶数次逆时针轮转。
故可利用双向循环链表建立存储结构,利用双向循环链表的遍历与删除操作实现功能要求。
3.概要设计1.抽象数据类型定义:typedef struct DuLNode{int data;struct DuLNode *prior;struct DuLNode *next;}DuLNode,*DuLinkList; //定义双向循环链表2.基本操作int InitList_Dul(DuLinkList &L) //建立一个只含头结点的空双向循环链表int CreateList_DuL(DuLinkList &L,int n) //建立一个带头结点的含n个元素的双向循环链表Lint ListDelete_DuL(DuLinkList &L,DuLinkList x) //删除指针x指向的结点3.设计思路首先建立一个双向循环链表作为存储结构,每个结点的数据域存储每个人的编号,然后根据给定的m值对整个链表进行遍历,找到所要找的结点后,输出该结点的数据域的值,并在双向循环链表中删除该结点,重复这样的操作,一直到所有的人都出列,依次输出的数列即为所求的Josephus排列。
4.详细设计typedef struct DuLNode{int data;struct DuLNode *prior;struct DuLNode *next;}DuLNode,*DuLinkList; //定义双向循环链表int InitList_Dul(DuLinkList &L) //建立一个只含头结点的空双向循环链表{L=(DuLinkList) malloc(sizeof(DuLNode));if(!L) return ERROR;L->data=0;L->next=L;L->prior=L;return OK;}int CreateList_DuL(DuLinkList &L,int n) //建立一个带头结点的含n个元素的双向循环链表L {DuLinkList p,q;int i;q=L;for(i=0;i<n;i++){p=(DuLinkList)malloc(sizeof(DuLNode));p->data=i+1; //m值的自动获取p->next=q->next;q->next=p;p->prior=q;L->prior=p;q=p;}return OK;}int ListDelete_DuL(DuLinkList &L,DuLinkList x) //删除指针x指向的结点{x->prior->next=x->next;x->next->prior=x->prior;free(x);return 0;}int main(){int n,m;int i=1;cin>>n;DuLinkList S;InitList_Dul(S);CreateList_DuL(S,n);cin>>m;DuLinkList a=S->next;//a指向第一个结点(不是头结点)if(m%2==1) //奇数次顺时针转{while(!(S->next==S->prior)) //当剩下最后一个人时(此时还有头结点)时退出循环{if(i==m){DuLinkList p;if(a->data==0)a=a->next;//跳过头结点p=a;a=a->next;cout<<p->data<<" ";ListDelete_DuL(S,p); //删除节点pi=1;}else{if(a->data==0)a=a->next;//跳过头结点a=a->next;i++;}}cout<<S->next->data<<endl; //输出最后一个出列人的的编号free(S->next);free(S); //释放除头结点和最后一个结点}else //偶数次逆时针转{while(!(S->next==S->prior)) //当剩下最后一个人时(此时还有头结点)时退出循环{if(i==m){DuLinkList p;if(a->data==0)a=a->prior; //跳过头结点p=a;a=a->prior;cout<<p->data<<" ";ListDelete_DuL(S,p); //删除节点pi=1;}else{if(a->data==0)a=a->prior; //跳过头结点a=a->prior;i++;}}cout<<S->next->data<<endl; //输出最后一个出列人的的编号free(S->next);free(S); //释放除头结点和最后一个结点}return 0;}5.调试分析1.遇到的问题:(1)开始对双向循环链表的删除操作的指针改变顺序出现了问题,导致删除结点时出现了错误;(2)双向循环链表中带有头结点,而头结点的数据域是空的(该程序中设为0),因此在对双向循环链表进行遍历和删除操作时,必须判断该结点是否是头结点,如果是,必须跳过该结点;2.收获:(1)通过对双向循环链表的建立、遍历、删除等操作的实现,对指针和链表了解得更加透彻,掌握得更加牢固;(2)对头结点问题的特殊处理,使自己解决问题的能力有了提升。
6.测试结果说明:若m是奇数,顺时针遍历双向循环链表;若m是偶数,逆时针遍历双向循环链表。
附录:程序源代码/*****************************************************************************约瑟夫环问题求解东北大学计算机科学与技术*****************************************************************************/ #include<iostream>#include<conio.h>#include<cstdlib>using namespace std;# define OK 1# define ERROR 0typedef struct DuLNode{int data;struct DuLNode *prior;struct DuLNode *next;}DuLNode,*DuLinkList; //定义双向循环链表int InitList_Dul(DuLinkList &L) //建立一个只含头结点的空双向循环链表{L=(DuLinkList) malloc(sizeof(DuLNode));if(!L) return ERROR;L->data=0;L->next=L;L->prior=L;return OK;}int CreateList_DuL(DuLinkList &L,int n) //建立一个带头结点的含n个元素的双向循环链表L {DuLinkList p,q;int i;q=L;for(i=0;i<n;i++){p=(DuLinkList)malloc(sizeof(DuLNode));p->data=i+1; //m值的自动获取p->next=q->next;q->next=p;p->prior=q;L->prior=p;q=p;}return OK;}int ListDelete_DuL(DuLinkList &L,DuLinkList x) //删除指针x指向的结点{x->prior->next=x->next;x->next->prior=x->prior;free(x);return 0;}int main(){while(1){ //主程序循环执行int n,m;int i=1;cout<<"请输入竞赛者人数n:"<<endl;cin>>n;DuLinkList S;InitList_Dul(S);CreateList_DuL(S,n);cout<<"请输入正整数m:"<<endl;cin>>m;cout<<"("<<n<<","<<m<<")"<<"Josephus排列(奇数次顺时针轮转,偶数次逆时针轮转)为:"<<endl;DuLinkList a=S->next;//a指向第一个结点(不是头结点)if(m%2==1) //奇数次顺时针转{while(!(S->next==S->prior)) //当剩下最后一个人时(此时还有头结点)时退出循环{if(i==m){DuLinkList p;if(a->data==0)a=a->next;//跳过头结点p=a;a=a->next;cout<<p->data<<" ";ListDelete_DuL(S,p); //删除节点pi=1;}else{if(a->data==0)a=a->next;//跳过头结点a=a->next;i++;}}cout<<S->next->data<<endl;//输出最后一个出列人的的编号free(S->next);free(S);//释放除头结点和最后一个结点}else//偶数次逆时针转{while(!(S->next==S->prior))//当剩下最后一个人时(此时还有头结点)时退出循环{if(i==m){DuLinkList p;if(a->data==0)a=a->prior;//跳过头结点p=a;a=a->prior;cout<<p->data<<" ";ListDelete_DuL(S,p); //删除节点pi=1;}else{if(a->data==0)a=a->prior;//跳过头结点a=a->prior;i++;}}cout<<S->next->data<<endl;//输出最后一个出列人的的编号 free(S->next);free(S);//释放除头结点和最后一个结点}} //while(1)return 0;}。