用双向循环链表求解约瑟夫环
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、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 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;}。