数据结构实验报告—约瑟夫问题求解
约瑟夫问题
一问题描述1 题目内容:约瑟夫(Joseph)问题的一种描述是:编号为1,2,..., n的n 个人按顺时针方向围坐一圈, 每人持有一个密码(正整数)。
一开始选任一个正整数作为报数上限值m,从第一个人开始按顺时针方向自1开始顺序报数,报到m时停止报数。
报m的人出列,将它的密码作为新的m值。
试设计一个程序求出出列顺序。
2 基本要求:利用单项循环链表存储结构模拟此过程,按照出列的顺序印出各人的编号。
3 测试数据:m的初值为20;n=7,7个人的密码依次为:3,1,7,2,4,8,4(正确的出列顺序应为6,1,4,7,2,3,5)。
二需求分析程序运行后,首先要求用户指定初始报数上限值,然后读取个人的密码。
输入数据:建立输入处理输入数据,输入m的初值,n ,输入每个人的密码,建立单循环链表。
输出形式:建立一个输出函数,将正确的输出序列三概要设计利用单项循环链表存储结构模拟此过程1 循环链表的抽象数据类型循环链表是单链表的一种变化形式,把单链表的最后一个节点的next指针指向第一个节点,整个链表就形成了一个环。
2 循环链表的基本操作(仅列出用在本程序的)creat(n)操作结果:构造一个长度为n的无头节点的循环链表,并返回指向最后一个节点的指针find(m,s)初始条件:循环链表存在操作结果:找到当前元素(即s)后面第m个元素print(&m,&n,&s)初始条件:循环链表存在操作结果:从s中删除约舍夫问题中下一个被删除的元素,并将此元素显示在屏幕上3 本程序包括4个模块:主程序模块;创建循环链表模块;找节点模块;删节点模块;各模块调用关系如下图所示:4 约舍夫问题的伪码算法void main( ){输入参与的人数;输入第一个密码;创建无头节点的循环链表;输出第一个出列元素;输出剩余出列元素;}四详细设计1 实现概要设计的数据类型typedef struct LNode{int data;int num;struct LNode *next;}LNode,*linklist; //无头节点的循环链表的节点类型2 每个子函数的算法linklist creat(int n){/*构造一个长度为n的无头节点的循环链表,并返回指向最后一个节点的指针*/linklist head,s; //head为头节点标记s为链表中节点int i;s=head=(linklist)malloc(sizeof(LNode)); //创建头节点for(i=1;i<n;i++) //建立循环链表{s->data=i;printf("num%d: ",i);scanf("%d",&(s->num));/*输入第i个人的密码*/while(s->num<=0){/*如果输入的s->num小于等于0,要求重新输入*/ printf("请重新输入\nnum%d: ",i);scanf("%d",&s->num);}s->next=(linklist)malloc(sizeof(LNode)); //开辟下一个节点s=s->next;}s->data=i;printf("num%d: ",i);scanf("%d",&(s->num));s->next=head;return(s);}linklist find(int m,linklist s) //找到当前元素后面第m个元素{int i;for(i=0;i<m-1;i++)s=s->next;return(s); //返回找到元素的指针}void print(into &mint &n,linklist &s){linklist p;s=find(m,s); //找到待删除的元素printf("%d ",s->next->data);/*输出找到的元素*/m=s->next->num;/*将此元素从链表中删除,并释放此节点*/ p=s->next;s->next=s->next->next;free(p);--n; //约舍夫环中节点数少一}3 主程序算法void main( ){/*解决约舍夫问题的主函数*/int n,m; //n为约舍夫环内初始人数m为初始密码printf("type in n :");scanf("%d",&n);/*输入n*/while(n<=0){/*如果输入的n小于等于0,要求重新输入*/printf("please type n in again \ntype in n :");scanf("%d",&n);}printf("type in m :");scanf("%d",&m);/*输入m*/while(m<0){/*如果输入的m小于0,要求重新输入*/printf("please type m in again \ntype in m :");scanf("%d",&m);}linklist s;s=creat(n);/*创建无头节点的循环链表,返回指向最后一个元素的指针*/printf("the sequence is ");print(m,n,s);//输出第一个出列的元素while(n){print(m,n,s);//输出剩余出列的元素}printf("\n");}4 函数调用关系图五调试分析调试过程中出现过如下问题:1 开始编程序时没考虑输入错误的问题,导致输入错误后程序出错2 编程序时删除节点子程序结束条件出错3 对开辟的节点用完后没有释放六使用说明程序运行后按提示输入n和m的值,在输入约舍夫环中每个人的密码,运行即可得到出列顺序七测试结果进入程序后要求输入n的值然后输入m的值再输入每个人的密码最后得到出列顺序八附录(源程序)这里附上两种源程序,本质上相同,只是第一个程序按老师要求写为很多子函数形式,第二个是我已开始编的,一个大函数。
数据结构实验报告
姓名:学号:班级:2010年12月15日实验一线性表的应用【实验目的】1、熟练掌握线性表的基本操作在顺序存储和链式存储上的实现。
、;2、以线性表的各种操作(建立、插入、删除、遍历等)的实现为重点;3、掌握线性表的动态分配顺序存储结构的定义和基本操作的实现;4、通过本章实验帮助学生加深对C语言的使用(特别是函数的参数调用、指针类型的应用和链表的建立等各种基本操作)。
【实验内容】约瑟夫问题的实现:n只猴子要选猴王,所有的猴子按1,2,…,n编号围坐一圈,从第一号开始按1,2…,m报数,凡报到m号的猴子退出圈外,如此次循环报数,知道圈内剩下一只猴子时,这个猴子就是猴王。
编写一个程序实现上述过程,n和m由键盘输入。
【实验要求】1、要求用顺序表和链表分别实现约瑟夫问题。
2、独立完成,严禁抄袭。
3、上的实验报告有如下部分组成:①实验名称②实验目的③实验内容:问题描述:数据描述:算法描述:程序清单:测试数据算法:#include <stdio.h>#include <stdlib.h>typedef struct LPeople{int num;struct LPeople *next;}peo;void Joseph(int n,int m) //用循环链表实现{int i,j;peo *p,*q,*head;head=p=q=(peo *)malloc(sizeof(peo));p->num=0;p->next=head;for(i=1;i<n;i++){p=(peo *)malloc(sizeof(peo));p->num=i;q->next=p;p->next=head;}q=p;p=p->next;i=0;j=1;while(i<n){if(j%m==0){q->next=p->next;p=q->next;printf("%4d",j%n);i++;}else{q=p;p=p->next;}j++;}printf("\n");}void main(){int n,m; /*人的个数*/printf("Please Enter n amd m(n>m):\n");scanf("%d%d",&n,&m);Joseph(n,m);}实验二树和图的应用【实验目的】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个人从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个数输出到屏幕上三.详细设计首先,设计实现约瑟夫环问题的存储结构。
实验一:约瑟夫问题
实验一:约瑟夫问题问题描述:用数组和链表存储方式实现约瑟夫问题。
约瑟夫问题:n个人围成一个圆圈,首先第1个人从1开始一个人一个人顺时针报数,报到第m个人,令其出列。
然后再从下一个人开始,从1顺时针报数,报到第m个人,再令其出列,…,如此下去,直到圆圈中只剩一个人为止。
此人即为优胜者。
基本要求:用顺序存储和链式存储方式实现。
试验报告内容:1.问题描述:设有n个人围坐在圆桌周围,现从某个位置m(1≤m≤n)上的人开始报数,报数到k 的人就站出来。
下一个人,即原来的第k+1个位置上的人,又从1开始报数,再报数到k的人站出来。
依此重复下去,直到全部的人都站出来为止。
2. 算法描述:可以先建一个单向循环链表;而整个“约瑟夫环”问题的过程,最终是把这个链表删空为止。
但在删时不能顺着删,而是按该问题的方案来删。
3.源程序#include <stdio.h>#include <stdlib.h>#define MAX_NODE_NUM 100#define TRUE 1U#define FALSE 0Utypedef struct NodeType{int id; /* 编号 */int cipher; /* 密码 */struct NodeType *next;} NodeType;/* 创建单向循环链表 */static void CreaList(NodeType **, const int);/* 运行 "约瑟夫环 "问题 */static void StatGame(NodeType **, int);/* 打印循环链表 */static void PrntList(const NodeType *);/* 得到一个结点 */static NodeType *GetNode(const int, const int);/* 测试链表是否为空, 空为TRUE,非空为FALSE */static unsigned EmptyList(const NodeType *);int main(void){int n, m;NodeType *pHead = NULL;while (1){printf( "请输入人数n(最多%d个): ", MAX_NODE_NUM); scanf( "%d ", &n);printf( "和初始密码m: ");scanf( "%d ", &m);if (n > MAX_NODE_NUM){printf( "人数太多,请重新输入!\n ");continue;}elsebreak;}CreaList(&pHead, n);printf( "\n------------ 循环链表原始打印 -------------\n "); PrntList(pHead);printf( "\n-------------- 出队情况打印 ---------------\n "); StatGame(&pHead, m);printf( "\n\ "约瑟夫环\ "问题完成!\n ");return 0;}static void CreaList(NodeType **ppHead, const int n){int i, iCipher;NodeType *pNew, *pCur;for (i = 1; i <= n; i++){printf( "输入第%d个人的密码: ", i);scanf( "%d ", &iCipher);pNew = GetNode(i, iCipher);if (*ppHead == NULL){*ppHead = pCur = pNew;pCur-> next = *ppHead;}else{pNew-> next = pCur-> next;pCur-> next = pNew;pCur = pNew;}}printf( "完成单向循环链表的创建!\n ");}static void StatGame(NodeType **ppHead, int iCipher){int iCounter, iFlag = 1;NodeType *pPrv, *pCur, *pDel;pPrv = pCur = *ppHead;/* 将pPrv初始为指向尾结点,为删除作好准备 */while (pPrv-> next != *ppHead)pPrv = pPrv-> next;while (iFlag) /* 开始搞了! */{/* 这里是记数,无非是移动iCipher-1趟指针! */for (iCounter = 1; iCounter < iCipher; iCounter++) {pPrv = pCur;pCur = pCur-> next;}if (pPrv == pCur) /* 是否为最后一个结点了 */iFlag = 0;pDel = pCur; /* 删除pCur指向的结点,即有人出列 */pPrv-> next = pCur-> next;pCur = pCur-> next;iCipher = pDel-> cipher;printf( "第%d个人出列, 密码: %d\n ",pDel-> id, /* 这个编号标识出列的顺序 */pDel-> cipher);free(pDel);}*ppHead = NULL; /* 没人了!为了安全就给个空值 */}static void PrntList(const NodeType *pHead){const NodeType *pCur = pHead;if (EmptyList(pHead))return;do{printf( "第%d个人, 密码: %d\n ", pCur-> id,pCur-> cipher); pCur = pCur-> next;} while (pCur != pHead);}static NodeType *GetNode(const int iId, const int iCipher){NodeType *pNew;pNew = (NodeType *)malloc(sizeof(NodeType));if (!pNew){printf( "Error, the memory is not enough!\n ");exit(-1);}pNew-> id = iId;pNew-> cipher = iCipher;pNew-> next = NULL;return pNew;}static unsigned EmptyList(const NodeType *pHead){if (!pHead){printf( "The list is empty!\n ");return TRUE;}return FALSE;}4.实验测试数据(要求有多组):第一组测试结果人数n为7, 初始密码m为20第1个人, 密码: 3第2个人, 密码: 1第3个人, 密码: 7第4个人, 密码: 2第5个人, 密码: 4第6个人, 密码: 8第7个人, 密码: 4-------------- 出队情况打印 ---------------第6个人出列, 密码: 8第1个人出列, 密码: 3第4个人出列, 密码: 2第7个人出列, 密码: 4第2个人出列, 密码: 1第3个人出列, 密码: 7第5个人出列, 密码: 4第二组测试结果人数n为8, 初始密码m为15第1个人, 密码: 5第2个人, 密码: 4第3个人, 密码: 3第4个人, 密码: 2第5个人, 密码: 9第6个人, 密码: 1第7个人, 密码: 7第8个人, 密码: 8-------------- 出队情况打印 ---------------第7个人出列, 密码: 7第6个人出列, 密码: 1第8个人出列, 密码: 8第3个人出列, 密码: 3第1个人出列, 密码: 5第4个人出列, 密码: 2第2个人出列, 密码: 4第5个人出列, 密码: 95.总结:1. 通过本次上机实践,对链表存储结构有了更深的理解和把握.2. 通过本次上机实践,应用链表的知识解决和分析问题的能力有了新的提高.3. 通过上机实践,掌握了用高级语言实现算法的基本步骤和方法.(最前面加班级、学号、姓名)。
数据结构实验一 约瑟夫环问题实验报告电子版
for(i = 1;i<length;i++){
tmp = (Node *)malloc(sizeof(Node));
tmp->number = num[i];
tmp->pass = pas[i];
pri->next = tmp;
pri = tmp;
pri->next = head;
for(i=0;i<time;i++){ //找到要删除的结点
tmp = tmp->next;
}
printf("%d ",tmp->number);
timeห้องสมุดไป่ตู้= tmp->pass - 1;
deleteFromList(&head,tmp);//删除结点
tmp = tmp->next;//从下一个结点又开始计算
initList(head);
createFromTail(head,num,pas,sizeof(num)/sizeof(num[0]));
p = head;
printf("\n约瑟夫计数前,每个数和他的密码:\n");
for(i = 0;i<sizeof(num)/sizeof(num[0]);i++){
}
}
// 从链表中删除
void deleteFromList(List *head,Node *tmp)
{
Node *tmp1;
Node *tmp2;
tmp1 = *head;
tmp2 = tmp1;
//如果链表剩了一个元素
实验一约瑟夫问题实验报告
北京邮电大学电信工程学院数据结构实验报告实验名称:实验一——约瑟夫问题学生姓名: ***班级: 20132111**班内序号: **学号: 201321****日期: 2014年1月4日1.实验要求实验题目:利用循环链表实现约瑟夫问题的求解。
约瑟夫问题如下:已知n个人(n>=1)围坐一圆桌周围,从1开始顺序编号。
从序号为1的人开始报数,顺时针数到m的那个人出列;他的下一个人又从1开始报数,数到m的那个人又出列;依此规则重复下去,直到所有人全部出列。
请问最后一个出列的人的编号。
实验目的:熟悉C++语言的基本编程方法,掌握集成编译环境的调试方法学习指针、模板类、异常处理的使用掌握线性表的操作的实现方法学习使用线性表解决实际问题的能力2. 程序分析2.1 存储结构采用单循环链表实现约瑟夫问题的求解单循环链表示意图2.2关键算法分析1、关键算法首先通过尾插法建立单循环链表,若只有一个人,即只删除该人即可,若多于一人,—则每查到m个人时删除该节点,并将循环链表连接好,共循环n-1次,每次删除均返回被删数值。
2、代码详细分析:1).指针结构、类的声明struct Node //创立节点{int number;Node *next;};class Joseph //建立Joseph类{private:int n;int m;Node *front ; //front头指针public:Joseph(int nn, int mm); //构造函数~Joseph(); //析构函数void Delete(); //删除函数};2).单循环链表的建立Joseph::Joseph(int nn, int mm) //构造函数,建立循环链表{n=nn;m=mm;Node *p,*rear; //建立两个指针.尾插法,p2始终指向为节点for(int i=1; i<=n; i++){p=new Node;p->number=i;if(i==1) //建立空链表{front=p;rear=p;}else{rear->next=p;rear=p;}}rear->next=front; //尾指向头,循环完成}算法:—①设两个指针p,rear, rear为尾节点,p为新增加的节点②若是空链表,则front=p; rear=p;③否则用尾插法,即p 在rear的后面,将p给rear;rear->next=p; rear=p;④头结点赋给rear的指针域,完成循环循环次数为n, 时间复杂度为O(n)3). 输入值异常的情况cout<<"请输入环内总人数n:";cin>>n;if (n<1) //考虑n输入错误的情况{cout<<"n值输入错误"<<endl;}cout<<"请输入m值:";cin>>m;if (m<1) //考虑m输入异常的情况{cout<<"m值输入错误"<<endl;}4).寻找一定间隔的人,并将其删除void Joseph::Delete() //删除人员的函数{Node *p1,*p2,*p;int count; //用来计数p1=front; //头结点给p1if(m==1)cout<<"最后一个人的编号为"<<n<<endl;else{cout<<"每次被删除的人为"<<endl;for(int i=1; i<=n-1; i++) //共删除n-1个人,循环n-1次{count=1;while(count<m){p2=p1; //p2始终为编号为1的那个人p1=p1->next; //p1向后移count++;}cout<<p1->number<<"\t"; //输出被删除的编号p=p1;p2->next=p1->next;p1=p1->next; //p1后移,防止删除后指针悬挂delete p;—}cout<<endl;cout<<"最后一个人的编号为"<<p1->number<<endl;front=p1;} }…………③图1 删除结点示意图算法⑤设p1、p2均指向第一个节点;⑥查找第m个节点,p1=p1—>next; p2始终指向第一个节点⑦摘链,将p指向待删除的p1,即将p1元素从链表中摘除:⑧输出p1的数值⑨释放p元素:delete p;循环次数为m(n-1), 时间复杂度为O(n)5)析构函数Joseph::~Joseph() //析构函数{delete front;front=NULL;}6)主函数void main(){int n,m;cout<<"请输入总人数n=";cin>>n;cout<<"请输入间隔m=";cin>>m;Joseph joseph(n,m);joseph.Delete();}3. 程序运行结果测试主函数流程:开始等待用户输入输入值是否有效是查找删除节点循环次数是否大于n-1次是输出最后一个数值结束流程图示意图1、测试条件:n数量级无限制2、测试结论4. 总结由于是第一次做数据结构的实验,而上学期的C++也因长时间没有碰过而稍显手生,在码程序的过程中遇到了很多问题,但通过翻看教材已基本解决。
数据结构与算法(Python版):用队列(Queue)处理约瑟夫问题
数据结构与算法(Python版):⽤队列(Queue)处理约瑟夫问题在古罗马时期,犹太⼈背叛了罗马⼈,落到困境,约瑟夫和同⾏的⼀共39个犹太⼈只能够⾃杀殉国,但是犹太教义规定不能⾃杀,因此只能够让别⼈将⾃⼰杀害。
他们所有39个⼈坐成⼀圈,报数1—7,报到7则由⾝旁的⼈将⾃⼰杀死。
结果约瑟夫灵机⼀动,给⾃⼰安排了⼀个位置,最后活了下来,那么约瑟夫给⾃⼰安排的是哪⼀个位置呢?在这个题⽬当中,我们如果使⽤队列,不仅可以处理任意⼈数坐成⼀圈,还可以将报数的值任意修改,最后都可以找到那⼀个不被杀死的⼈的位置。
我们可以将所有⼈都放进⼀个⼤的队列⾥,每报⼀次数字,那么就把队列头部的⼈放到队列的尾部,直到报数报到⼀组数字的最后⼀个,⽐如1——7当中的7。
这个时候就将队列头的这个⼈删除(也就是杀死),不断执⾏这个过程,直到整个队列当中的⼈数只有⼀个,则跳出循环返回最后活着的那个⼈的名字。
⾸先定义队列(Queue)类的结构:class Queue():def__init__(self):# 初始化⼀个空的列表self.__list=[]# 往队列⾥插⼊元素def enqueue(self,item):self.__list.append(item)# 弹出队列⾥的元素def dequeue(self):return self.__list.pop(0)# 弹出队列⾥最先进⼊的元素# 判断队列是否为空def is_empty(self):return self.__list == []# 计算队列的⼤⼩def size(self):return len(self.__list)使⽤队列类来初始化⼀个对象,sim_queue,然后编写刚才我们分析之后的程序:def hot_potato(namelist,num):sim_queue = Queue()for name in namelist:sim_queue.enqueue(name) # 把拿到的名字全部都放到队列⾥while sim_queue.size() > 1:for i in range(num):sim_queue.enqueue(sim_queue.dequeue())# 每执⾏完⼀次,就将队列的头拿出来弹出,相当于⼟⾖传递给这个⼈,然后这个⼈就死了last_person=sim_queue.dequeue()return last_personprint("开始执⾏约瑟夫问题")print(hot_potato(["bob","NAni","Ao li Gei!","HeHe","Mike","Suvennia"],4))输出:开始执⾏约瑟夫问题Ao li Gei!得解,因此Ao li Gei!这个⼈不会被杀死。
数据结构约瑟夫环问题
数据结构实验报告题目:约瑟夫环问题一.设计内容[问题描述]约瑟夫环问题的一种描述是:编号为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。
约瑟夫环 实验报告
约瑟夫环实验报告约瑟夫环实验报告引言:约瑟夫环是一个经典的数学问题,它源自于古代传说。
根据传说,古代犹太人被罗马人围困在一个洞穴中,他们决定用一种特殊的方式来决定谁将成为首领。
他们站成一个圆圈,从一个人开始,每隔一个人杀掉一个,直到只剩下一个人。
这个问题被称为约瑟夫环问题,它在数学领域引起了广泛的研究和探讨。
实验目的:本实验旨在通过模拟约瑟夫环问题,探讨其数学规律和解法,并分析实验结果的意义和应用。
实验步骤:1. 首先,我们需要确定参与约瑟夫环的人数n和每次报数的间隔m。
在本次实验中,我们选择了n=10和m=3。
2. 接下来,我们将10个人按顺序排成一个圆圈,并给每个人编号,编号从1到10。
3. 实验开始时,从第一个人开始报数,每次报数到m的人将被淘汰出局。
4. 淘汰的人将离开圆圈,下一个人将从淘汰者的下一个人开始报数,继续进行报数和淘汰的过程,直到只剩下一个人为止。
实验结果:通过模拟实验,我们得到了以下结果:- 第一轮淘汰的人依次为:3、6、9、2、7、1、8、5、10。
- 第二轮淘汰的人依次为:4、9、2、8、5、1、7、6。
- 第三轮淘汰的人依次为:9、8、5、1、7、4、6。
- 第四轮淘汰的人依次为:1、7、4、6、9、5。
- 第五轮淘汰的人依次为:7、4、6、9、5。
- 第六轮淘汰的人依次为:4、6、9、5。
- 第七轮淘汰的人依次为:6、9、5。
- 第八轮淘汰的人依次为:9、5。
- 第九轮淘汰的人依次为:5。
结论:通过实验结果的分析,我们可以得出以下结论:1. 在本次实验中,最后幸存的人是编号为5的人。
2. 根据实验结果,我们可以总结出约瑟夫环问题的一般解法。
假设总人数为n,每次报数的间隔为m,最后幸存的人的编号可以通过递归公式f(n,m)=[f(n-1,m)+m]%n得到。
3. 约瑟夫环问题在数学中具有一定的研究价值和应用意义。
它涉及到递归、数论等数学概念和方法,可以帮助我们更好地理解和应用这些数学知识。
约瑟夫问题详解(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]。
约瑟夫环问题实验报告
//报数为m的人出列
while(n--)
{
for(int s=m-1; s--; r=p, p = p->link);
cout << "The output is: " << p->data << endl;
r->link = p->link;
LinkList d = new LNode;
if(!d)
二、实验问题描述
设编号为1,2,···,n的n个人围坐一圈,约定编号为k(1≤k≤n)的人从1开始报数,数到m的那个人出列,他的下一位又从1开始报数,数到m的那个人又出列,依次类推,直到所有人出列为止,由此产生一个出队编号的序列。
3、实验步骤
1、实验问题分析
①由于当某个人退出圆圈后,报数的工作要从下一个人开始继续,剩下的人仍要是围成一个圆圈,可以使用循环表;由于退出圆圈的工作对应着表中结点的删除操作,对于这种删除操作频繁的情况,应该选用效率较高的链表结构;为了程序指针每一次都指向一个具体的代表一个人的结点而不需要进行判断,链表不带表头结点。所以,对于所有人围成的圆圈所对对应的数据结构采用一个不带头结点的循环链表来描述。设头指针为p,并根据具体情况移动
可以采用数据类型定义: Typedef struct node {
int number;
struct node *next; }Lnode,*Linklist;
②为了记录退出的人的先后顺序,采用一个顺序表进行存储,程序结束后再输入依次退出的人的编号顺序。由于只记录各个结点的number值就可以,所以定义一个整型一维数组。如“int quite[N];”N为一个根据实际问题定义的一个足够大的整数。
约瑟夫问题实验报告
约瑟夫问题实验报告(文章一):约瑟夫问题数据结构实验报告中南民族大学管理学院学生实验报告实验项目: 约瑟夫问题课程名称:数据结构年级:专业:信息管理与信息系统指导教师:实验地点:管理学院综合实验室完成日期:小组成员:学年度第(一)、实验目的(1)掌握线性表表示和实现;(2)学会定义抽象数据类型;(3)学会分析问题,设计适当的解决方案;(二)、实验内容【问题描述】:编号为1,2,…,n 的n 个人按顺时针方向围坐一圈,每人持有一个密码(正整数)。
一开始任选一个正整数作为报数上限值m,从第一个人开始按顺时针方向自 1 开始顺序报数,报到m 时停止报数。
报m 的人出列,将他的密码作为新的m 值,从他在顺时针方向上的下一个人开始重新从1 报数,如此下去,直至所有人全部出列为止。
试设计一个程序求出出列顺序。
【基本要求】:利用单向循环链表存储结构模拟此过程,按照出列的顺序印出各人的编号。
【测试数据】:m 的初值为20;密码:3,1,7,2,4,8,4(正确的结果应为6,1,4,7,2,3,5)。
(三)、实验步骤(一)需求分析对于这个程序来说,首先要确定构造链表时所用的方法。
当数到m 时一个人就出列,也即删除这个节点,同时建立这个节点的前节点与后节点的联系。
由于是循环计数,所以才采用循环列表这个线性表方式。
程序存储结构利用单循环链表存储结构存储约瑟夫数据(即n个人的编码等),模拟约瑟夫的显示过程,按照出列的顺序显示个人的标号。
编号为1,2,?,n 的n 个人按顺时针方向围坐一圈,每人持有一个密码(正整数)。
一开始任选一个正整数作为报数上限值m,从第一个人开始按顺时针方向自1 开始顺序报数,报到m 时停止报数。
报m 的人出列,将他的密码作为新的m 值,从他在顺时针方向上的下一个人开始重新从1 报数,如此下去,直至所有人全部出列为止。
试设计一个程序求出出列顺序。
基本要求是利用单向循环链表存储结构模拟此过程,按照出列的顺序印出各人的编号。
约瑟夫环实验报告
约瑟夫环实验报告约瑟夫环(Josephus problem)是一个非常经典的数学问题,其得名于公元1世纪的犹太历史学家约塞夫斯(Josephus)。
约瑟夫环问题描述如下:n个人围坐成一个圆圈,从一些人开始依次报数,每报到第m个人,该人就被淘汰出圆圈,然后从下一个人重新开始报数。
直到剩下最后一个人时,即为问题的解。
例如,当n=7,m=3时,最后剩下的是4号人。
本次实验的目的是研究约瑟夫环问题的解决方法,并通过编程实现给定n和m的情况下找到最后的获胜者。
首先,我们需要分析问题的特点。
当n=1时,该问题的解即为最后剩下的人;当n>1时,最后剩下的人可以通过前一轮问题的解(剩下n-1个人的情况下)推导出来。
我们可以将解决该问题的方法分为两种:递归法和迭代法。
一、递归法递归法是通过问题的子问题来解决原问题。
对于约瑟夫环问题来说,递归法的解题思路如下:1.当n=1时,问题的解即为1;2.当n>1时,问题的解为(找到n-1个人时的解+m-1)对n取模,即((f(n-1,m)+m-1)%n)+1二、迭代法迭代法通过循环来解决问题,不断更新当前的解,直到问题得到解决。
对于约瑟夫环问题来说,迭代法的解题思路如下:1.初始化一个长度为n的数组a,a[i]=1表示第i个人还在圆圈中,a[i]=0表示第i个人已经被淘汰出圆圈;2. 从第一个人开始计数,每报数到第m个人,则将该人设为已淘汰,并计数器count加1;3. 重复步骤2,直到count=n-1;4.循环遍历数组a,找到最后剩下的人。
为了更加直观地展示实验结果,我们通过Python编写下述代码:```python#递归法解决约瑟夫环问题def josephus_recursive(n, m):if n == 1:return 1else:return (josephus_recursive(n - 1, m) + m - 1) % n + 1#迭代法解决约瑟夫环问题def josephus_iterative(n, m):a=[1]*ncount = 0i=0while count < n - 1:if a[i] == 1:j=0while j < m:if a[(i + j) % n] == 1:j+=1else:j=0i=(i+1)%na[(i-1)%n]=0count += 1for i in range(n):if a[i] == 1:return i + 1#测试递归法解决约瑟夫环问题print(josephus_recursive(7, 3)) # 输出4 #测试迭代法解决约瑟夫环问题print(josephus_iterative(7, 3)) # 输出4 ```通过以上代码,我们可以得到n=7,m=3时,最后剩下的人是4号人。
线性表实验报告
LinkList p;
p=L->next;
while(p){
printf("%5d",p->data);
p=p->next;
}
return OK;
}
void main()
{
int i,n,k,d,e;
LinkList La,Lb;
InitList_L(La);
InitList_L(Lb);
e=L.elem[i];
for(j=i;j<=L.length;j++)
L.elem[j]=L.elem[j+1];
L.length--;
return ok;
}
int output(sqlist &L){
int i;
printf("output sqlist data:\n");
for(i=0;i<L.length;i++)
PrintList(La);
printf("\nAfter delete the list is:\n");
ListDelete_L(La,e);
PrintList(La);
printf("\n");
printf("I will insert:");
scanf("%d",&k);
ListInsert_L(La,k);
int a,j,i,m;
CLinkList p,r;
printf("Input the m(m<=20):\nm=");
scanf("%d",&m);
约瑟夫问题(算法与数据结构课设报告)
线性表的操作及其应用一、问题描述线性表、队列是一种常用的数据结构,有顺序和链式两种存储结构,在实际中应用十分广泛,而链表又分为单链表和循环链表,队列又分为链式队列和循环队列。
这些数据结构都可用来解决约瑟夫环问题。
约瑟夫环问题是算法设计中的一个经典问题,是顺序编号的一组n个人围坐一圈,从第1个人按一定方向顺序报数,在报到m时该人出列,然后按相同方法继续报数,直到所有人出列。
设计算法求约瑟夫环中人员的出列顺序。
二、基本要求1、选择合适的存储结构,建立线性表;2、利用顺序存储结构求解约瑟夫环问题;3、利用单链表和循环链表分别求解约瑟夫环问题;4、利用队列求解约瑟夫环问题。
三、测试数据约瑟夫环的测试数据为7,报数为1至3。
四、算法思想由于用到四种不同的存储结构,它们的算法思想依次是:1、首先建立一个顺序表模拟整个约瑟夫环,手动输入顺序表长(即参加约瑟夫循环的人数)和循环的次数和表元素。
用已经输出总人数和顺序表长作比较,作为外层循环条件。
并对每一个输出后的元素重新赋值以为标记。
对于每次循环,首先检查顺序表此次是不是我们设立的标记,如果不是则循环次数加1,当达到要求的循环次数时就将循环次数设置为0,输出该元素到屏幕并将总输出元素加1。
每次外循环我们都会移到表的下一个位置,作为新的判断条件,每次报到表尾的时候,我们都将重新设置到表尾,作为下次循环的表元素。
2、首先采用链式循环链表建立整个约瑟夫环,手动输入第一次的循环次数和每个人所持下一个循环次数。
设立判断指针指向表头,并将该指针是否为空作为外层循环条件。
做一个内层循环,将判断指针移动到循环要输出的数,并设立一个前指针指向该指针的前一个位置,输出该元素后,将循环次数重新赋值成该元素。
接着判断前指针和判断指针比较,如果相等说明整个表已经输出完毕,否则将删除该位置的元素。
3、用链式队列建立循环约瑟夫环,手动输入人数,第一次的循环次数和每个人所持下一个循环次数。
并将每一个元素依次入队列,根据第一次循环次数,建立一个for循环,每一次循环都出队列,如果达到要求的循环次数就输出,否则进队列,这样这个数字就出现在队尾。
约瑟夫环问题 实验报告完整版
{
int data;//数据域
Node *next;//next指针指向下一个结点
};
3.算法设计
问题要求建立模型,确定存储结构,之后对任意n个人,密码为m,实现约瑟夫环问题,出圈的顺序可以依次输出,也可以用一个数组存储。
设计流程图如图1.1所示。
图1.1设计流程图
(1)创建循环链表
{
p=p->next;
}
q=p->next;
p->next=q->next;
p=p->next;
printf("第%3d个出圈的人是:%3d\n",i,q->value);
free(q);
}
scanf("\n");
p->next=NULL;
}
(3)主程序执行
主程序运行,调用函数,程序接受数据后,输出出圈列数。
}
(2)约瑟夫环报数的算法在运行为循环方式,报数者除非本身已经出去,否则继续顺序报数,其报数循环的代码为
void Joseph(NODE *p,int number,int n)
{
int i,j;
NODE *q=NULL;
for(i=1; i<=number; i++)
{
for(j=1; j<n-1; j++)
由于内容的要求以及问题的方便,用循环链表作为本次实验的抽象数据类型。申请一个结点作为第一个结点,之后调用creat_list函数将后续结点一次插入链接,构造为循环链表。
NODE *link(int number)
{
NODE *head=NULL,*p=NULL,*q=NULL;
约瑟夫环与八皇后问题--数据结构课程设计实验报告
录 问题描述 1 问题分析 1 数据结构描述 算法设计 2 详细程序清单 程序运行结果 心得体会 12
1 4 11
一、 问题描述 1. 约瑟夫问题描述 编号为1,2... n的n个人按顺时针方向围坐一圈,每人持有一个密码(正整数)。一 开始任选一个正整数作为报数的上限值m,从第一个人开始按顺时针方向自1开始顺序报 数,报到m时停止报数,报m的人出列,将他的密码作为新的m值,从他的顺时针方向上的 下一个开始重新从1报数,如此下去,直至所有人全部出列为止,设计一个程序求出出列 顺序。 2. 八皇后问题描述 在一个8×8的棋盘里放置8个皇后,要求每个皇后两两之间不相"冲"(在每一横 列竖列斜列只有一个皇后)。 3、界面设计模块问题描述 设计一个菜单式界面,让用户可以选择要解决的问题,同时可以退出程序。界面要 简洁明了,大方得体,便于用户的使用,同时,对于用户的错误选择可以进行有效的处 理。 二、 问题分析 在整个课程设计中,我主要负责的是约瑟夫问题中链表中的出列的操作算法的设计。 用循环单链表表示编号为1,2... n的n个人按顺时针方向围坐一圈,每人持有一个密码 (正整数)。一开始输入一个正整数作为报数的上限值turn,从第一个人开始按顺时针方 向自1开始顺序报数(即从第一个结点开始指针向后移动),报到turn-1时(即指针指向 turn-1个结点时)停止,他的下一位出列,将他的下一位密码作为新的turn值,从出列的 人的的顺时针方向上的下一个开始重新从1报数,如此下去,直至链表中只剩一位(即一 个结点)退出循环,并所有人的编号按出列顺序输出。在实现的过程中定义i表示报数的
int code; struct LNode *next; }node,*linklist; linklist creatstart(linklist L,int number) { int m,i; linklist s,p; s=L; for(i=1;i<=number;i++) { p=(linklist)malloc(sizeof(node)); if(!p) exit(0); p->data=i; printf("please input the code of number %d:",i); scanf("%d",&p->code); p->next=NULL; s->next=p; s=p; } s->next=L->next; return s; } void chulie(linklist L,int number) { int turn,i,j; linklist p,s; printf("please input the start code:"); scanf("%d",&turn); p=L; printf("the turn out of the circle is:"); for(i=1;i<=number-1;i++) { for(j=1;j<=turn-1;j++) p=p->next; printf("%d ",p->next->data); turn=p->next->code; s=p->next; p->next=s->next; free(s); } printf("%d ",p->next->data); printf("\n"); } void lianbiao() { int number; linklist L; L=(linklist)malloc(sizeof(node));
用顺序表解决约瑟夫环问题
⽤顺序表解决约瑟夫环问题⼀、实验题⽬:约瑟夫环问题:设编号为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;}。
约瑟夫环问题的两种解法(循环链表和公式法)
约瑟夫环问题的两种解法(循环链表和公式法)问题描述这⾥是数据结构课堂上的描述: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),显然,这不是⼀个好的算法。
最新实验一约瑟夫问题实验报告
最新实验一约瑟夫问题实验报告实验目的:探究约瑟夫问题(Josephus Problem)的数学规律及其在不同参数下的表现,验证相关算法的效率和准确性。
实验背景:约瑟夫问题是一个著名的理论问题,源自于罗马时代的一个传说。
问题可以描述为:n个人围成一圈,从第一个人开始报数,每数到第m个人,该人出圈,然后从下一个人重新开始报数,如此循环,直到所有人出圈。
本实验旨在通过编程模拟这一过程,并分析结果。
实验方法:1. 采用编程语言(如Python)编写约瑟夫问题的模拟程序。
2. 设定不同的n和m值,运行程序,记录每个人的出圈顺序及最后剩下的人的位置。
3. 分析不同n和m值下的出圈顺序规律。
4. 对比不同算法(如递归法、迭代法)的运行时间,评估效率。
实验步骤:1. 初始化参数:确定模拟的总人数n和报数间隔m。
2. 创建一个循环队列模拟人们围成的圈。
3. 通过循环和条件判断模拟报数和出圈过程。
4. 记录每次出圈的人的编号和最终剩下的人的位置。
5. 改变n和m的值,重复步骤1至4,收集多组数据。
6. 分析数据,寻找出圈规律。
7. 对模拟过程进行计时,比较不同算法的运行时间。
实验结果:1. 通过大量实验数据,发现当n和m的值较小时,可以直观看出出圈顺序的规律。
2. 随着n和m值的增大,出圈顺序变得更加复杂,但依然存在一定的规律性。
3. 实验中使用的迭代法在处理大规模数据时,相比递归法具有更高的效率,递归法在深度较大时可能会导致栈溢出。
4. 通过图表展示了不同n和m值下,最后剩下的人的位置的概率分布。
实验结论:1. 约瑟夫问题的出圈顺序并非完全随机,存在一定的数学规律。
2. 迭代法在解决大规模约瑟夫问题时更为高效和稳定。
3. 本实验为进一步研究约瑟夫问题提供了实验数据和算法优化方向。
建议:对于未来的研究,可以尝试将约瑟夫问题推广到更多变种,如双向报数、不同方向报数等,以及探索其在实际问题中的应用,如网络协议设计、资源分配等。
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
《计算机软件技术基础》实验报告 I —数据结构实验一、约瑟夫斯问题求解一、问题描述1.实验题目:编号 1,2,....,n的n个人顺时针围坐一圈,每人持有一个密码(正整数)。
开始选择一个正整数作为报数上限m,从第一个人开始顺时针自 1 报数,报到m的人出列,将他的密码作为新的m值,从他在顺时针方向下一个人开始重新从 1 报数,直至所有人全部出列。
2. 基本要求:利用单向循环链表存储结构模拟此过程,按照出列的顺序印出个人的编号。
3. 测试数据: n=7,7 个人的密码依次为:3,1,7,2,4,8,4.m初值为6(正确的出列顺序应为 6,1,4,77,2,3)。
二、需求分析1. 本程序所能达到的基本可能:该程序基于循环链表来解决约瑟夫问题。
用循环链表来模拟n 个人围坐一圈,用链表中的每一个结点代表一个人和他所代表的密码。
在输入初始密码后m,对该链表进行遍历,直到第 m个结点,令该结点的密码值作为新的密码值,后删除该结点。
重复上述过程,直至所有的结点被释放空间出列。
2. 输入输出形式及输入值范围:程序运行后提示用户输入总人数。
输入人数 n 后,程序显示提示信息,提示用户输入第i个人的密码,在输入达到预定次数后自动跳出该循环。
程序显示提示信息,提示用户输入初始密码,密码须为正整数且不大于总人数。
3.输出形式提示用户输入初始密码,程序执行结束后会输出相应的出列结点的顺序,亦即其编号。
用户输入完毕后,程序自动运行输出运行结果。
4.测试数据要求:测试数据 n=7,7 个人的密码依次为:3, 1, 7, 2, 4, 8, 4。
m初值为 6(正确的出列顺序应为6, 1, 4,7, 2, 3, 5)。
三、概要设计为了实现上述功能,应用循环链表来模拟该过程,用结构体来存放其相应的编号和密码信息,因此需要循环链表结构体这个抽象数据类型。
1.循环链表结构体抽象数据类型定义:ADT Node{数据对象: D={ai,bi,ci|ai∈ int,bi ∈ int,ci∈(Node*),i=1,2...,n,n ≥0}:数据关系: R=?基本操作:CreatList(int n)// Order(int m,node *l)}ADT node;构建循环单链表;// 输出函数,输出出列顺序并删除链表中的结点;2.ADT 的 C 语言形式说明:typedef struct Node{int num;//结点的数据域,存放编号;int word; //结点的数据域,存放密码;struct Node *next; //结点的指针域,存放指向下一结点的指针;}Node;Node *CreatList( )// 建立循环单项链表;void Order(Node *h) //输出出列顺序并删除结点;3.主程序流程及其模块调用关系:1) . 主程序流程:先提示用户输入相关数据:总人数,运行循环链表结构体模块,输入每个人持有的密码值,创建出新链表,运行输出函数模块,再输入初始密码m值,输出出列序列。
(创建循环单链表模块:实现链表的抽象数据类型删除链表结点输出序列模块:实现输出出列顺序)2) . 模块调用关系:主函数模块创建循环链表结体模块构删除链表结点输出序列模块四、详细设计1.元素类型、结点类型和结点指针类型:typedef struct Node//定义一个结构体,包含了每个人的序号和密码{int word;int num;struct Node *next;}Node;2、创建单向循环链表类型:Node *CreatList()//尾插法创建一个单向循环链表{Node *p,*s;Node *h;//定义头指针h=(Node*)malloc(sizeof(Node));//申请动态存储空间p=h;h->next=NULL;//初始化链表printf("第 1 个人的密码是:");scanf("%d",&h->word);h->num=1;//给头指针赋值for(i=0;i<n-1;i++){s=(Node*)malloc(sizeof(Node));if(h->next==NULL) h->next=s;else p->next=s;p=s;printf("第 %d个人的密码是:",i+2);scanf("%d",&s->word);s->num=i+2;}p->next=h;return h;}3.删除链表结点输出函数模块:void Order(Node *h)//输出结果{Node *p;p=h;Node *d;while(p->next!=p){if(m<2){p=p->next;}else{for(i=1;i<m-1;i++) //查找第m个结点{p=p->next;}}d=p->next;m=d->word;printf("%d--",d->num);p->next=d->next; //删除结点p=p->next;free(d);}printf("%d\n",p->num);}4. 主函数:void main(){printf("试验名称:约瑟夫斯问题求解\n");printf("学号: \n");printf("姓名: xx\n");printf("=========================================================\n");time_t rawtime1;struct tm * timeinfo1;time (&rawtime1);timeinfo1 = localtime (&rawtime1); //时间函数;printf ("程序运行开始, 当前日期和时间: %s", asctime(timeinfo1));printf("请输入总人数:");scanf("%d",&n);h=CreatList();printf("请输入初始密码:");scanf("%d",&m);if(m>n){printf("初始密码不得大于总人数,请重新输入。
");scanf("%d",&m);Order(h);}else{Order(h);}time_t rawtime2;struct tm * timeinfo2;time (&rawtime2);timeinfo2 = localtime (&rawtime2);printf ("程序运行结束,当前日期和时间: %s", asctime(timeinfo2));while(1);}5.函数调用关系:主函数调用Node *CreatList()创建循环单向链表以及调用void Order(Node *h)进行删除结点及输出序列功能。
输出出列序列输出以后,函数调用结束。
五、调试分析1. 程序中将每个人的编号以及密码信息放在链表节点中的数据域,使密码和序号一一对应。
主函数中,只需要调用链表的操作来实现循环单向链表的创建以及删除结点和输出操作。
2.算法的时空分析:由于链表采用的都是单向循环链表,而链表中按结点访问的时间性能O( n), n 是链表中结点的个数。
所以,CreatList()以及Order(Node*h)算法的时间复杂度都是O( n)。
输入数据后界面:八、遇到的问题和解决方法:1. 起初程序编写好后显示0 error,但不能正常运行,显示如图出现随机数是因为头指针的数据域没有赋值。
增加了对头指针数据域赋值语句后随机数消失。
3.在调试的开始发现输出顺序有错误,经调试发现是因为查找第m个结点时所用的for循环是从i=1 到 i<m-1 的,但第二个人密码为1,没有考虑m<2的情况,于是输出出错。
改正方式是增加一个m<2的特殊情况。
经运行,输出成功。
九、实验收获和感想:约瑟夫问题是我们学习了软件工程原理与应用这门课后第一次上机编写程序,在以前接触c语言的时候,我们是没有学过链表的,因此,以前也没有编写过循环链表的程序。
本学期的软件工程原理与应用开课后,我才真正深入学习和理解了链表结构,通过此次编程对所学知识有了具体的运用,使我对链表结构有了更清晰的了解,从茫然到能正确运用,感觉收获非常大。
约瑟夫问题虽说是链表问题中相对来说最简单的一个问题,但我刚开始时却是充满了茫然,编出来的程序处处报错。
起初,并没有真正理解建立链表的算法,我单纯仿着课本上的代码照搬上去,不会正确的运用,结果自然是大量的错误。
于是我放下急于求成的心态,认真看书,认真查资料,终于成功地写出了这个约瑟夫问题的程序。
当它最终0 error 通过且运行正常时,我的心中充满了激动和满足感,看着自己的作品,很有成就感。
编写完整的程序最考验人的耐心和细心,每一个微不足道的小错误都会导致很长时间的排错。
这次的计算机实践使我在运用中理解了循环链表这部分的知识,为后面的学习和接下来的计算机实践打好了基础,奠定了基石。
十、附录源程序文件清单:#include<stdio.h>#include<malloc.h>#include <time.h>#define NULL 0typedef struct Node// 定义一个结构体,包含了每个人的序号和密码{int word;int num;struct Node *next;}Node;int i,n,m;Node *h;Node *CreatList()// 尾插法创建一个单向循环链表{Node *p,*s;Node *h;// 定义头指针h=(Node*)malloc(sizeof(Node));// 申请动态存储空间p=h;h->next=NULL;// 初始化链表printf(" 第 1 个人的密码是:");scanf("%d",&h->word);h->num=1;// 给头指针赋值for(i=0;i<n-1;i++){s=(Node*)malloc(sizeof(Node));if(h->next==NULL)h->next=s;else p->next=s;p=s;printf(" 第 %d 个人的密码是:",i+2);scanf("%d",&s->word);s->num=i+2;}p->next=h;return h;}void Order(Node *h)// 输出结果{Node *p;p=h;Node *d;while(p->next!=p){if(m<2){p=p->next;}else{for(i=1;i<m-1;i++)// 查找第 m 个结点{p=p->next;}}m=d->word;printf("%d--",d->num);p->next=d->next;// 删除结点p=p->next;free(d);}printf("%d\n",p->num);}void main(){printf(" 试验名称:约瑟夫斯问题求解\n");printf(" 学号: \n");printf(" 姓名: xx\n");printf("=========================================================\n");time_t rawtime1;struct tm * timeinfo1;time (&rawtime1);timeinfo1 = localtime (&rawtime1);// 时间函数;printf (" 程序运行开始,当前日期和时间 : %s", asctime(timeinfo1));printf(" 请输入总人数: ");scanf("%d",&n);h=CreatList();printf(" 请输入初始密码:");scanf("%d",&m);if(m>n){printf(" 初始密码不得大于总人数,请重新输入。