大数据结构实验一约瑟夫问的题目
数据结构-约瑟夫实验
1.实验题目:顺序表的应用2.实验内容:约瑟夫环问题:设编号为1,2,3,……,n的n(n>0)个人按顺时针方向围坐一圈,m为任意一个正整数。
从第一个人开始顺时针方向自1起顺序报数,报到m时停止并且报m的人出列,再从他的下一个人开始重新从1报数,报到m时停止并且报m的人出列。
如此下去,直到所有人全部出列为止。
要求设计一个程序模拟此过程,对任意给定的m和n,求出出列编号序列。
3.设计分析首先定义一个数组a[],用来存储1~n这些整数;然后建立顺序表,将a数组中的元素存储在顺序表中;然后,就是约瑟夫函数的实现,用i和t分别来存储顺序表L中元素的个数和出列者的编号,然后用t = (t + m - 1) % i;语句来改变t值来输出相应位置的元素,再使用for 循环语句,将后一个元素前移,并将其作为初始的元素重新开始循环。
4.源程序代码#include<stdio.h>#include<malloc.h>#define MaxSize 100typedef int ElemType; //定义ElemType为int类型typedef struct //定义顺序表的存储类型{ElemType data[MaxSize];int length;}SqList;void CreateList(SqList * &L,ElemType a[],int n) //建立顺序表{int i;L=(SqList *)malloc(sizeof(SqList));for (i=0;i<n;i++)L->data[i]=a[i];L->length=n;}void InitList(SqList * &L) //初始化顺序表{L=(SqList * )malloc(sizeof(SqList));L->length=0;}void DestroyList(SqList * &L) //销毁顺序表{free(L);}int Josephus(SqList * L,int m,int n) //约瑟夫环的实现{int i, j;int t = 0; //首次报数的起始位置if (m<1 || m>L->length)return 0;else{printf("输出顺序为:\n");for (i = n; i >= 1; i--) //i为顺序表L中元素的个数{t = (t + m - 1) % i; //t为出列者的编号printf("%d\t", L->data[t]); //编号为t的元素出列for (j = t + 1; j <= i-1; j++) //后面的元素前移一个位置L->data[j-1] = L->data[j];}printf("\n");}return 1;}int main(void){int m,n;int i;int a[MaxSize];SqList * L;printf("请输入任意的正整数n: ");scanf("%d",&n);printf("请输入任意的正整数m(m<=n): ");scanf("%d",&m);for (i=0;i<n;i++){a[i]=i+1;}InitList(L); //调用InitList()函数CreateList(L,a,n); //调用CreateList()函数Josephus(L,m,n); //调用Josephus()函数DestroyList(L); //调用DestroyList()函数return 0;}5.测试用例(尽量覆盖所有分支)第一个:第二个:第三个:6.实验总结通过这次实验,我了解到了约瑟夫环的相关操作,而且,对链表的知识有了进一步的了解,加深了对以前学习链表相关知识的理解,虽然在编写和调试程序的过程中出项了很多问题,如:在使用malloc 函数前没有加malloc.h的头文件;改变出列者的编号问题开始不知道如何去写;还有就是用完链表后没有释放该链表等一些问题。
数据结构与算法分析(1)——约瑟夫问题
数据结构与算法分析(1)——约瑟夫问题0 问题描述 据说著名犹太历史学家 Josephus有过以下的故事:在罗马⼈占领乔塔帕特后,39 个犹太⼈与Josephus及他的朋友躲到⼀个洞中,39个犹太⼈决定宁愿死也不要被敌⼈抓到,于是决定了⼀个⾃杀⽅式,41个⼈排成⼀个圆圈,由第1个⼈开始报数,每报数到第3⼈该⼈就必须⾃杀,然后再由下⼀个重新报数,直到所有⼈都⾃杀⾝亡为⽌。
然⽽Josephus 和他的朋友并不想遵从。
⾸先从⼀个⼈开始,越过k-2个⼈(因为第⼀个⼈已经被越过),并杀掉第k个⼈。
接着,再越过k-1个⼈,并杀掉第k个⼈。
这个过程沿着圆圈⼀直进⾏,直到最终只剩下⼀个⼈留下,这个⼈就可以继续活着。
问题是,给定了和,⼀开始要站在什么地⽅才能避免被处决?Josephus要他的朋友先假装遵从,他将朋友与⾃⼰安排在第16个与第31个位置,于是逃过了这场死亡游戏。
[1] 简单地说就是n个⼈围成⼀个圈,假设编号为0~n-1,报数范围为1~m。
从第0个⼈开始报数,每报到m,这个⼈就出局。
接着下⼀个⼈从1开始重新报数...直到剩下最后⼀个⼈。
1 问题解法 该问题常⽤解法有链表法和数组法,其他的⽐如公式法和递归法以后补充。
个⼈觉得链表法最容易理解,可能是先⼊为主的⼼理,嘿嘿嘿,下⾯就简单说⼀下。
1.1 链表法 ⾸先建⽴长度为n的循环链表,然后头结点开始报数,每第m个数删除⼀个结点,直到只剩下⼀个结点。
#include <iostream>using namespace std;struct ListNode{int val;struct ListNode* next;};/******创建循环链表******/ListNode* creatCirListNode(int n){//创建头结点ListNode *head,*node,*newnode;node = new ListNode;node->val = 0;node->next = NULL;head = node;//创建链表for (int i = 1; i < n; i++){newnode = new ListNode;newnode->val = i;node->next = newnode;node = newnode;}//将最后⼀个结点指向头结点,形成循环链表node->next = head;return head;}/******约瑟夫问题求解******/int josephSolution(ListNode* head,int m){if (NULL == head){return -1;}ListNode* pNode; //⽤来遍历结点pNode = head;while (pNode->next != pNode) //循环终⽌条件是只剩下⼀个结点{for (int i = 0; i < m-1; i++) //报数{pNode = pNode->next;}cout << pNode->next->val << "->";pNode->next = pNode->next->next; //删除链表结点}return pNode->val;}int main(){int n = 41;int m = 3;int result;ListNode* pHead;pHead = creatCirListNode(n);result = josephSolution(pHead, m);cout << result << endl;system("pause");return0;} 运⾏结果:1.2 数组法 数组法的关键是使⽤求余的⽅法循环遍历数组i=(i+1)%n,其中i是数组下标即每个⼈的标号,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. 通过上机实践,掌握了用高级语言实现算法的基本步骤和方法.(最前面加班级、学号、姓名)。
约瑟夫环数据结构实验
实验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的人,再令其出列。
如此下去,直到圈中所有人出列为止。
求出列编号序列。
基本要求需要基于线性表的基本操作来实现约瑟夫问题需要利用顺序表来实现线性表输入输出格式输入格式:n,m输出格式1:在字符界面上输出这n个数的输出序列输出格式2:将这n个数的输出序列写入到文件中测试用例(举例)输入:10,3输出:3 6 9 2 7 1 8 5 10 4课后选做内容(1)使用单链表来实现之(2)使用循环链表来实现之课后习题请以O(n)的时间复杂度来实现约瑟夫问题。
HUNAN UNIVERSITY实验报告题目:约瑟夫环问题学生姓名**学生学号***********专业班级 ******* 指导老师****完成日期****/**/**一、需求分析(1)编号为1-n的n个人按顺时针方向围成一圈.首先第1个人从1开始顺时针报数.报m的人(m 为正整数).令其出列。
然后再从他的下一个人开始,重新从1顺时针报数,报m的人,再令其出列。
实验一约瑟夫环问题
实验四约瑟夫环问题一、问题描述设有编号为1,2,…n的n(n>0)个人围成一个圈.每个人持有个密码m。
从第1个人开始报数,报到m时停止报数,报m的人出圈,再从他的下个人起重新报数,报到m时停止报数,报m的出圈……如此下去,直到所有人全部出圈为止。
当任意给定n和m后,设计算法求n个人出圈的次序。
二、基本要求(1)建立模型,确定存储结构(2)对任意n个人,密码为m,实现约瑟夫环问题(3)出圈的顺序可以依次输出,也可以用一个数组存储三、实验步骤1.需求分析本演示程序用VC++编写,生成一个循环链表,输入人数输入密码,在执行报数输出操作,输出报数序列;①输入的形式和输入值的范围:输入的总共报数人数以及报数密码,在所有输入中,输入数据都为正整数。
②输出的形式:在输出报数顺序是输出报数顺序序号对应报数本身编号。
输出完成后输出是否重新进行提示信息。
③程序所能达到的功能:任意输入的报数人数people,及出列密码num输出报数出列顺序编号。
④测试数据:A.输入报数人数:50,初始化循环链表;B.输入报数密码:3,输出报数出列序列2.概要设计1)为了实现上述程序功能,需要定义循环链表的抽象数据类型:ADT LinkList {数据对象:D={ai|ai∈IntegerSet,i=0,1,2,…,n,n≥0}数据关系:R={<ai,ai+1>|ai,ai+1 ∈D}基本操作:creatcirlink(int n)操作结果:建立循环链表并进行初始化report_num(cirlink &hand,int m,int max)初始条件:循环链表hand已建立;操作结果:报数输出相应的元素data;Game()初始条件:循环链表hand已建立;操作结果:输入报数人people数及密码numMenu()操作结果:在屏幕上显示操作菜单}2)本程序包含4个函数:①主函数main()②显示操作菜单函数menu()③报数出列函数report_num ()④输入人数和密码函数Game()3.详细设计设计实现约瑟夫环问题的存储结构。
实验一、约瑟夫问题
实验一:约瑟夫问题求解一、问题描述1、实验题目:约瑟夫(Josephus)问题的一种描述是:编号为1,2,……,n的n个人按顺时针方向围坐一圈,每人持有一个密码(正整数)。
一开始任选一个正整数作为报数上线值m,从第一个人开始按顺时针方向自1开始报数,报到m时停止报数。
报m的人出列,将他的密码作为新的m值,从他在顺时针方向下一个人开始重新从1报数,如此下去,直至所有的人全部出列为止。
2、基本要求:试设计一个程序,按出列顺序印出个人编号。
3、测试数据:m的初值为20;n=7,7个人的密码依次为:3,1,7,2,4,8,4。
m的初值为6,正确的出列顺序应为:6,1,4,7,2,3,5。
二、需求分析1、本程序用来求出含有密码的约瑟夫问题,可以输出所有人的出列顺序。
2 、程序运行后显示提示信息,提示用户输入一圈的人数n,接着输入每个人的密码,最后提示输入初始密码。
3、用户输入完毕后,程序自动输出运算结果。
三、概要设计1、设计思路n个人围成一圈,每个人的手中都有一个密码,这个密码决定了下一次报数的上限。
游戏规则:①给定一个初始密码②循环报数,报到密码值的人要出列,依次类推,直到所有的人都出列本程序要求输入的内容:n个人的密码及初始密码;本程序要求输出的内容:n个人出列的顺序。
2、数据结构为了实现上述功能,可以采用链式存储结构。
采用链式存储结构,定义了一个存储个人信息的结构体,及两个自定义函数,分别用于创建链表和约瑟夫出列操作。
①链表抽象数据类型的定义: #define SLNODE struct slnodeADT SLNODE{数据对象:D={ i a |i a ∈SLNODE, i=1,2,3.... }数据关系:R=φ}ADT SLNODE;②自定义函数:void create_SLnode(SLNODE *p,int n)//创建队列{ 创建链表,为N 个人分配密码 }void Josef(SLNODE *p,int n)//进行约瑟夫操作{输入初始密码m;for(){ 将出列的结点删除,并输出出列序号;}}③本程序的保护模块:结构体模块主程序模块自定义函数模块调用关系:3、程序设计主要算法的流程图:create_SLnode( )算法流程图Josef( )算法流程图四、详细设计1、元素类型、结点的类型及指针#define SLNODE struct slnodeSLNODE//每个结点的结构体{int num;//num代表序号int code;//code代表密码SLNODE *next;};2、自定义函数:void create_SLnode(SLNODE *p,int n)//创建队列,并将其尾指针指向第一个序号{SLNODE *r,*s;s=p;int i,m;cout<<"请给这"<<n<<"个人分配密码:"<<endl;for(i=0;i<n;i++){cout<<"请给第"<<i+1<<"个人输入密码:"<<endl;cin>>m;r=(SLNODE *)malloc(sizeof(SLNODE));r->code=m;r->num=i+1;r->next=s->next;s->next=r;s=s->next;}p=p->next;s->next=p;}void Josef(SLNODE *p,int n)//进行约瑟夫操作{p=p->next;int m;int i,j;SLNODE *r;cout<<"请输入初始密码:"<<endl;cin>>m;cout<<"依次出列的序号为:"<<endl;for(i=0;i<n-1;i++)p=p->next;for(i=0;i<n-2;i++){for(j=0;j<m-1;j++)p=p->next;cout<<(p->next)->num<<endl;m=(p->next)->code;r=p->next;p->next=r->next;}if(m%2==0)cout<<p->num<<endl<<(p->next)->num<<endl;elsecout<<(p->next)->num<<endl<<p->num<<endl;}3、主函数:int main(){SLNODE *p;int n;cout<<"请输入一圈的人数:"<<endl;cin>>n;p=(SLNODE *)malloc(sizeof(SLNODE));p->next=NULL;create_SLnode(p,n);Josef(p,n);return 0;}4、函数的调用关系:主函数main()调用自定义函数void create_SLnode(SLNODE *p,int n);/*创建队列*/与void Josef(SLNODE *p,int n);/*进行约瑟夫操作*/。
数据结构经典题(一)Josephus(约瑟夫)问题
Josephus(约瑟夫)问题有n个人围成一个圈,从第1个人开始报数,数到第m个人,让他出局;然后从出局的下一个人重新开始报数,数到第m个人,再让他出局,……,如此反复直到剩下一个人,问此人编号为几?或:有n个人围成一个圈,从第k个人开始报数,数到第m个人,让他出局;然后从出局的下一个人重新开始报数,数到第m个人,再让他出局,……,如此反复直到所有人出列,由此产生一个出队编号的序列。
1、数组解法#include<iostream>#include<stdlib.h>using namespace std;const int n=11, m=3;int main(){int a[n],p=0;int i,k=0,number=0;for(i=0; i<n; i++) a[i]=i+1;while(number<n-1) //number表示出去的人数{ if(a[p]!=0) //p指向正要报数的人{ k++; //k为1,2,3...报数if(k==m) //报到m时,a[p]出去{ a[p]=0; k=0; number++; }}p=(p+1) % n; //下一个人}for(i=0; i<n; i++)if(a[i]!=0){ cout<<"最后一个获胜者的编号是:"<<i+1<<endl; break; }system("pause");}其中while循环也可改为:while(number<n-1) //number表示出去的人数{while(a[p]==0) p=(p+1) % n; //找到下一个报数的人k++; //k为1,2,3...报数if(k==m) //报到m时,a[p]出去{ a[p]=0; k=0; number++; }p=(p+1) % n;}2、链表解法#include<iostream>#include<stdlib.h>using namespace std;const int n=11, m=3;struct node{ int no;node *next;};int main(){int k=0;node *p,*q,*r;p=q=new node; //创建第一个节点p->no=1;for(int i=2; i<=n; i++) //建立链表{ r=new node;r->no=i;q->next=r;q=r;}q->next=p; //构成一个"环"q=p;while(q->next!=q){ k++; //k为1,2,3...报数if(k==m) //报到m时,删除q所指结点{ p->next=q->next;delete q;q=p->next;k=0;}else{ p=q; q=q->next; }}cout<<"最后一个获胜者的编号是:"<<q->no<<endl; system("pause");}其中while循环也可改为:while(q->next!=q){ for(int i=1; i<m; i++) //直接找到报m的人{ p=q; q=q->next; }p->next=q->next;delete q;q=p->next;}。
数据结构实验一约瑟夫(Joseph)问题
华北#########学院数据结构实验报告2011~2012学年第二学期级计算机专业班级:学号:姓名:实验一线性表及其应用一、实验题目:线性表及其应用——约瑟夫环二、实验内容:1.设带头结点的单链表ha和hb中结点数据域值按从小到大顺序排列,且各自链表内无重复的结点,要求:(1)建立两个按升序排列的单链表ha和hb。
(2)将单链表ha合并到单链表hb中,且归并后的hb链表内无重复的结点,结点值仍保持从小到大顺序排列。
(3)输出合并后单链表hb中每个结点的数据域值。
代码:实验结果:struct Node{ int data;Node* next; };typedef Node Slink;void create(Slink* h);void show(Slink* h);void merge(Slink* ha,Slink* hb);#include<iostream.h>void main(){cout<<"创建链表ha"<<endl;Slink ha; ha.next =NULL;create(&ha);cout<<"链表ha的节点排列"<<endl;show(&ha);cout<<endl;cout<<"创建链表hb"<<endl;Slink hb; hb.next =NULL;create(&hb);cout<<"链表hb的节点排列"<<endl;show(&hb);cout<<endl;cout<<"合并后链表hb的节点排列"<<endl;merge(&ha,&hb);show(&hb);}void create(Slink* h){if(!h) return;int n=0;//节点总数int j=0;//累计创建结点个数cout<<"请输入创建节点的个数"<<endl;cin>>n;Node* F=h;//F始终指向tou节点Node* pre=h;//pre始终指向要插入位置的前一个节点while(j<n){Node* p=new Node;cout<<"请输入节点的数据"<<endl;cin>>p->data;//链表为空时if(!F->next ){ F->next =p;p->next =NULL;j++;continue;}//链表不空时while(pre->next ){ if(p->data <pre->next ->data ){ p->next =pre->next ;pre->next =p;pre=h;j++;break;}else if (p->data ==pre->next ->data){ cout<<"该值已存在,请重新输入"<<endl;break;}pre=pre->next ;}if(!pre->next ){ pre->next =p;p->next =NULL;j++;}}}void merge(Slink* ha,Slink* hb){ //p遍历ha,q遍历hbNode * p=ha->next ;Node * q=hb->next ;//pw始终指向新生成链表的最后一个结点Node * pw=hb;while(p&&q){ if((p->data)<(q->data)){ pw->next=p;p=p->next;pw=pw->next;continue;}if((p->data)>(q->data)){ pw->next=q;q=q->next;pw=pw->next;continue;}if((p->data)==(q->data)){ pw->next=q;p=p->next;q=q->next;pw=pw->next;continue;}}while(p){ pw->next=p;p=p->next;pw=pw->next;}while(q){ pw->next=q;q=q->next;pw=pw->next;}pw->next=NULL;}void show(Slink* h){Node* p=h->next ;while(p ){ cout<<p->data <<" ";p=p->next ;}cout<<endl;}2.约瑟夫(Joseph)问题。
约瑟夫问题大全
“约瑟夫”问题及若干变种林厚从例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),对于极限数据会超时。
数据结构实验报告—约瑟夫问题求解
《计算机软件技术基础》实验报告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值,输出出列序列。
数据结构实验报告约瑟夫环
数据结构实验报告约瑟夫环约瑟夫环是一个古老而有趣的问题,也是数据结构中一个经典的应用。
它的故事发生在公元前1世纪,当时犹太人正面临罗马的入侵。
为了避免被俘虏,一群犹太士兵决定以一种特殊的方式自杀,而不是被罗马人俘虏。
他们围成一个圈,按照某个规则进行自杀,直到只剩下一个人为止。
这就是著名的约瑟夫环问题。
在这个问题中,我们有n个人,编号从1到n,围成一个圈。
按照一定的规则,从第一个人开始报数,每次报到m的人将被淘汰。
然后,从下一个人开始重新报数,如此循环,直到只剩下一个人为止。
这个问题的解决方法有很多,其中最常见的是使用链表数据结构。
我们可以将每个人表示为一个节点,节点之间通过指针连接,形成一个环形链表。
每次淘汰一个人后,只需要将指针跳过被淘汰的节点,重新连接链表。
为了更好地理解这个问题,我们可以通过一个简单的例子来演示。
假设有10个人,编号从1到10,每次报数到3的人将被淘汰。
首先,我们将这10个人表示为一个环形链表:1->2->3->4->5->6->7->8->9->10->1。
按照规则,第一次报数到3的人是3号,所以我们将3号节点从链表中删除:1->2->4->5->6->7->8->9->10->1。
接下来,从4号节点开始重新报数。
第二次报数到3的人是6号,所以我们再次将6号节点从链表中删除:1->2->4->5->7->8->9->10->1。
以此类推,直到只剩下一个人为止。
通过这个例子,我们可以看到约瑟夫环问题的解决方法非常简单直观。
使用链表数据结构,每次淘汰一个人后,只需要将指针跳过被淘汰的节点,重新连接链表。
这种方法的时间复杂度为O(n*m),其中n为人数,m为报数的次数。
除了链表,还有其他数据结构可以用来解决约瑟夫环问题。
数据结构 实验1约瑟夫问题
实验一、约瑟夫问题求解一、顺序结构代码如下:#include "stdio.h"#include "stdlib.h"#define MAXSIZE 100typedef struct node{int data[MAXSIZE];int length;}SeqList,*PSeqList;int Delete(PSeqList PL,int i){int j;if(!PL){printf(" 表不存在");return (-1);}if(i<1||i>PL->length){printf(" 删除位置不合法");return(0);}for(j=i;j<PL->length;j++){PL->data[j-1]=PL->data[j];}PL->length--;return(1);}PSeqList Init(void){int n,i;PSeqList PL;PL=(PSeqList)malloc(sizeof(SeqList));if(PL) {PL->length=0;}printf(" 请输入圆桌人数:\n");scanf("%d",&n);if(n>MAXSIZE)printf(" 溢出");for(i=0;i<n;i++){PL->data[i]=i;PL->length++;}return PL;}int yuesefu(PSeqList yuesefu,int s,int m){int s1,i,w;if(!yuesefu->length){printf(" 表中无元素");return(0);}s1=s-1;printf(" 输出约瑟夫序列:\n");for(i=yuesefu->length;i>0;i--){s1=(s1+m-1)%i;w=yuesefu->data[s1];printf("%d\t",w);Delete(yuesefu,s1+1);}return(1);}void main(){PSeqList PL;int m,n;PL=Init();printf(" 请分别输入起始序号和计数值:\n");scanf("%d%d",&m,&n);yuesefu(PL,4,3);}运行结果如下:二、链式结构代码如下:#include <stdio.h>#include <stdlib.h>#include <cstdlib>typedef struct node{int data;struct node* next;}Lnode,*Linklist;Linklist create(int n){int i;Linklist head;Linklist p1,p2;p1=p2=(Linklist)malloc(sizeof(Lnode));p1->data=0;head=p1;for(i=1;i<n;i++){p2=p1;p1=(Linklist)malloc(sizeof(Lnode));p1->data=i;p2->next=p1;}p1->next=head;return(head);}int yuesefu(Linklist yuesefu,int s,int m){Linklist p,pre;int count;if(!yuesefu){printf("表中无元素");return(0);}p=yuesefu;for(count=1;count<s;count++){p=p->next;}printf("输出约瑟夫序列:\n");while(p!=p->next){pre=p->next;while(pre->next!=p) {pre=pre->next;}for(count=1;count<m;count++){pre=p;p=p->next;}printf("%d\t",p->data);pre->next=p->next;free(p);p=pre->next;}printf("%d\t",p->data);free(p);return(1);}int main(){Linklist PL;int s,m,n;printf("请输入圆桌的人数,起始位置,间隔\n");scanf("%d%d%d",&s,&m,&n);PL=create(s);yuesefu(PL,m,n);return(1);}运行结果如下:。
数据结构实验--约瑟夫环问题
线性表及其应用班级:软件101 姓名:xxx 学号:xxxxxxx 完成日期:2011-11-18 题目:编制一个求解约瑟夫环问题的程序一、需求分析//该题目的功能等需求、测试数据以及预期的输出结果等。
问题描述:编号为1,2,…,n的n个人按顺时针方向围坐一圈。
每人持有一个密码(正整数),一开始任选一个正整数作为报数上限值m,从第一个人开始按顺时针方向自1开始报数,报到m时停止报数。
报m的人出列,将他的密码作为新的m值,从他顺时针方向的下一个人开始重新从1报数,直至所有人全部出列为止。
试设计一个程序求出出列顺序。
(1):在本实验中,要求利用单向循环链表存储结构来完成,以便有效地掌握线性表相关知识及其应用。
(2):在程序运行后,首先指定一个初始报数的上限值m=20,然后输入各人的密码,假设n<=10;(3):编译执行后得到结果并进行检查核对。
(4):测试数据为:初始密码2,各成员密码分别为:2、7、1、8、、2、8、5 ;结束条件:输入0二、概要设计//数据结构的大概设计;程序模块的设计以及大概算法等。
为了实现上述程序功能,应以循环链表存储结构来表示;需要抽象数据类型有:(1):线性链表存储结构的定义struct JosephNode{int number;//编号int password;//密码struct JosephNode *next;};(2):数据结构类型的定义typedef struct JosephNode *JosephCircle;JosephCircle Init(void);JosephCircle CountOff(JosephCircle joseph , int& number , int& password); typedef struct JosephNode *PJoseph;三:详细设计//结构在程序设计语言中的表示;各函数的完整说明;所需函数的概要代码;函数调用关系等。
约瑟夫问题及完整代码
实验约瑟夫问题1、问题描述编号为1,2,……,n的n个人按顺时针方向围坐一圈,每人持有一个密码(正整数)。
一开始任选一个正整数作为报数上限值m,从第一个人开始按顺时针方向自1开始顺序报数,报到m时停止报数。
报m的人出列,将他的密码作为新的m值,从他在顺时针方向上的下一个人开始重新从1报数,如此下去,直至所有人全部出列为止。
试设计一个程序求出出列顺序。
2、数据结构设计n个人围圈,形成线性关系;处理为逐个删除,故用链式结构合适;又人员围成圆圈,所以此链式结构采用循环方式较好;排号按照一个方向进行,故数据结构采用带头结点的单向循环链表。
假设人员以首次的编号命名,对每个人员采用编号和密码加以描述。
利用单向循环链表存储结构模拟此过程,按照出列的顺序印出各人的编号。
#include<iostream>using namespace std;typedef struct node{int no;int key;struct node *next;}node,*LinkList;LinkList CreateList(int *tt,int nn){LinkList h = (node*)malloc(sizeof(node));h->next = h;node *p,*q;p=h;for(int i1 = 0;i1<nn;i1++){q=(node*)malloc(sizeof(node));q->no = i1+1;q->key = tt[i1];p->next = q;p = q;}p->next = h;p = NULL;q = NULL;return h;}void DeleteList(LinkList pre) //pre为要删除节点的前一个节点{LinkList m;m = pre->next;pre->next = pre->next->next;free(m);m = NULL;}void run(LinkList L,int kk){int z = kk;LinkList x = L;int ct = 0;while(L->next != L){for(ct = z-1;ct>0;ct--) //使x指向要删除节点的前一个节点{if(x->next == L){x = L;}x = x->next;}if(x->next == L){x = L;}cout<<x->next->no<<" ";z = x->next->key;DeleteList(x);}}void main(){int k,n;cout<<"输入第一个密码:"<<endl;cin>>k;cout<<"输入人数:"<<endl;cin>>n;int *t;t = new int[n];cout<<"输入"<<n<<"个人的密码"<<endl;for(int i = 0;i<n;i++){cin>>t[i];}LinkList list = CreateList(t,n);run(list,k);delete [] t;system(“pause”);}。
约瑟夫问题数据结构实验报告
约瑟夫问题数据结构实验报告正文:⒈引言本实验报告旨在探讨约瑟夫问题及其相关数据结构的实现。
约瑟夫问题是一个经典的数学问题,涉及到环形数据结构和循环删除等操作。
通过本次实验,我们将实现约瑟夫问题的求解算法,并对其进行性能分析。
⒉算法描述⑴问题描述约瑟夫问题描述如下:有n个人围成一圈,从某个人开始依次报数,报到m的人出圈,然后从出圈的下一个人开始重新报数,如此循环,直到剩下最后一个人。
需要实现一个算法解决以下问题:(1)给定n和m,求出最后剩下的人的编号。
(2)给定n和m,按顺序输出出圈的人的编号。
⑵算法思路本实验将使用环形链表作为数据结构存储人员编号。
首先,创建一个含有n个节点的环形链表,每个节点表示一个人的编号。
然后,按照报数规则不断遍历链表,当报数等于m时,删除当前节点,并将游标指向下一个节点。
重复此过程,直到只剩下一个节点。
最后剩下的节点即为问题的解。
⒊算法设计⑴定义数据结构为了实现约瑟夫问题的求解,我们需要设计以下数据结构:(1)Person:表示每个人的节点,包含编号和指向下一个节点的指针。
(2)CircularLinkedList:表示环形链表,包含人员总数,当前游标位置和指向第一个节点的指针。
⑵算法实现(1)创建环形链表根据输入的人员总数n,创建一个含有n个节点的环形链表,每个节点表示一个人。
初始化时,节点的编号为从1到n,指向下一个节点的指针为空。
(2)解决约瑟夫问题通过循环遍历环形链表,按照报数规则逐个删除节点,直到只剩下一个节点为止。
具体步骤如下:①初始化报数计数器count=1,游标指向链表的第一个节点。
②从游标指向的节点开始,依次报数,每报数一次,count加1.③当count等于m时,删除当前节点,并将游标指向下一个节点。
④重复步骤②和③,直到只剩下一个节点。
(3)输出解决结果根据求解约瑟夫问题的要求,输出最后剩下的人的编号或按顺序输出出圈人的编号。
⒋性能分析本算法的时间复杂度为O(nm),其中n表示人员总数,m表示报数的步长。
实验报告1约瑟夫环
《数据结构》实验报告班级:姓名:学号:日期:08-10-20题目:约瑟夫环一、上机实验的问题和要求:问题描述:编号为1到n的n个人围成一圈,每人带一个密码c,以m为报数上限。
然后从第一个人开始顺时针自1开始报数,报到m的人出列,将其密码作为新的m值,从他的下一个人开始,同样顺时针自1开始报数,依次循环下去,直到所有的人都出列!要求得到依次出列的那些人的编号序列!基本要求:用C代码实现此活动,要求使用一定的算法进行操作,并最终通过程序运算出最后的结果!二、程序设计的基本思想,原理和算法描述:(包括程序的模块结构,数据结构,输入/输出设计,符号名说明等)基本思想:利用不带头结点单向循环链表模拟该活动,在实现了一切动作之后,运用一定的算法得到最终的结果。
程序的模块结构:定义了相关的结构体之后,主要有以下几大模块:1.建立由头指针指示的有n个结点的不带头结点的单向循环链表crt_CLinkList(H,n);2.寻找、输出、删除H单循环链表的第m个结点locfor(H,m);3.最后通过main函数体处理参数的输入与显示,并调用以上两函数,最终解决问题。
主要数据结构:单链的循环链表,即线性表的链式存储结构。
算法的伪码描述:结构体定义如下:typedef struct Lnode /*定义单链表*/{int number; /*编号*/int cipher; /*密码*/struct Lnode *next; /*指针*/}Lnode,*CLinklist; /*重定向命名*/CLinklist H; /*H为全局单链表*/算法的实现详见源程序。
输入/输出设计本程序并未采用任何二进制文件出入的方式,这点说明将在最后总结提到。
至于函数的出入口问题,在源程序及注释中将得到详细说明,这里不再赘述。
三、源程序及注释:(说明函数功能、入口及出口参数,其他)#include <stdio.h> /* 头文件*/#include <stdlib.h>#include <alloc.h>typedef struct Lnode /*定义单链表*/{int number; /*编号*/int cipher; /*密码*/struct Lnode *next; /*指针*/}Lnode,*CLinklist; /*重定向命名*/CLinklist H; /*H为全局单链表*/struct Lnode *crt_CLinkList(CLinklist H,int m) /*创建一个不带头结点的单向循环链表*/{ /*其中,H为全局单链表,m为链表结点总数*/ int i; /*循环记数用*/struct Lnode *ptr1; /*用于索引*/if((ptr1=(struct Lnode *)malloc(sizeof(struct Lnode)))==NULL) /*一旦动态内存分配失败,报错退出!*/ {perror("malloc");return ptr1;}H=ptr1; /*形成单个结点的单循环链表*/ptr1->next=H;for(i=1;i<m;i++) /*形成m个结点的不带头结点的单循环链表*/ {if((ptr1->next=(struct Lnode *)malloc(sizeof(struct Lnode)))==NULL){perror("malloc");ptr1->next=H;return H;}ptr1=ptr1->next; /*其中H指头,ptr指尾*/ptr1->next=H;}return H; /*返回成功新建的单链表H*/}void locfor(CLinklist H,int m) /*寻找输出删除链表H中的第m个结点*/{ /*H为全局链表,m为要查找删除的结点位置*/ int i; /*循环计数*/struct Lnode *ptr;for(i=1;i<=5;i++) /*考虑图形界面的美观问题*/printf("number\tcipher\t");i=1; /*初始化*/while(H->next!=H) /*只剩单个结点时停止循环,进行最后输出!*/ {if(m==1) /*考虑进行自身删除的情况,即m=1*/{for(ptr=H->next;ptr->next!=H;ptr=ptr->next);/*正因为是自身删除才要费大劲寻找其父结点*/printf("%-6d\t",H->number); /*找到后,先输出*/printf("%-6d\t",H->cipher);m=H->cipher; /*确定下一次寻找的m值*/ptr->next=H->next; *删除结点,即自身结点*/ptr=ptr->next;free(H); /*因为对于链表中的结点,每个之前都分配了内存,所以free是必要的*/H=ptr; /*不同于以下普通结点的删除,自身删除还要还原H*/i=1; /*i重置,以方便下一次的循环操作*/}else if(i==m-1) /*因为从自身开始即为1号,因为m-1,而不是m*/{ptr=H->next; /*结点的删除操作同于上面的情况!*/printf("%-6d\t",ptr->number);printf("%-6d\t",ptr->cipher);m=ptr->cipher;H->next=ptr->next;H=H->next;free(ptr);i=1;}else{H=H->next; /*若未找到,则继续遍历!*/i++;}}printf("%-6d\t",H->number); /*对于单个结点的单循环链表,进行最终的输出操作!*/ printf("%-6d",H->cipher);free(H); /*完成所有任务并释放所有内存占用!*/}void main() /*主函数接口*/{ /*调用所有函数,进行实验模拟,并得出实验结果!*/ int i,j,n=30,m,k;struct Lnode *ptr;randomize(); /*因为下面调用了random函数,故此处的初始化很有必要!*/ printf("Now the experiment will begin.You have two choices!\n");/*数据输入可以电脑随机,也可以自行输入*/printf("If you want to enter the datas all by yourself,please enter 1.\n");/*自行输入(方便检测程序问题)*/ printf("If you want to get the datas by the computer,please enter 0.\n"); /*电脑随机产生数据*/printf("Now your choice:"); /*用户选择*/scanf("%d",&j);while(j!=0&&j!=1) /*考虑程序的完善性,对于误输入的提示并报警!*/ {printf("\nYou enter is unillage!Please try again!\n");printf("Now your choice:"); /*重新输入*/scanf("%d",&j);}H=crt_CLinkList(H,n); /*初始化链表*/if(j==0) /*电脑随机充入数据*/for(i=1;i<=30;i++){H->number=i;H->cipher=rand(); /*随机函数*/H=H->next;}else /*此时选择实验者输入数据!*/{for(i=1;i<=30;i++){H->number=i;printf("Now number %d,please enter the cipher:",i);scanf("%d",&k);H->cipher=k;H=H->next;}}m=rand(); /*默认情况下,m随机产生*/printf("Do you want to enter the number m?Enter 1 to set or others cancel!\n");/*当然,如果想自已输,可以进行覆盖*/scanf("%d",&k);if(k==1) /*自行输入m值*/{printf("Please enter the number m:");scanf("%d",&m);}system("cls"); /*考虑屏幕大小,进行分屏显示*/printf("All followed are your datas!\n"); /*界面友善*/for(i=1;i<=5;i++)printf("number\tcipher\t");for(i=0;i<30;i++) /*显示所有处理前数据*/{printf("%-6d\t",H->number);printf("%-6d\t",H->cipher);H=H->next;}printf("And the number m is :%d\n",m);printf("\nAfter the program,the result is:\n");locfor(H,m); /*对所有数据进行实验处理,直至所有结点处理完!*/ getchar();printf("\nPress any key return!"); /*TC环境下,方便查看结果!*/getchar();}四、用户使用说明与测试运行结果:1.运行程序后,首先弹出界面,截图如右:理性化的选择:如果打1,则所有的cipher均由用户输入,这样方便对特殊数据进行程序测试!如果打0,那么所有的数据均由电脑产生!那如果打其他的呢?考虑到误输入,加了一个循环,以提高程序的健壮性!2.首先自行输入数据进行测试。
数据结构实验报告一-约瑟夫环问题
实验1约瑟夫环问题1.需求分析(1)输入的形式和输入值的范围:每一次输入的值为两个正整数,中间用逗号隔开。
若分别设为n,m,则输入格式为:“n,m”。
不对非法输入做处理,即假设输入都是合法的。
(2)输出的形式:输出格式1:在字符界面上输出这n个数的输出序列输出格式2:将这n个数的输出序列写入到文件中(3)程序所能达到的功能:对于输入的约瑟夫环长度n和间隔m,输出约瑟夫环的出列顺序。
(4)测试数据:包括正确的输入及其输出结果和含有错误的输入及其输出结果。
正确:输入:10,3输出:3 6 9 2 7 1 8 5 10 4输入:41,3输出:3 6 9 12 15 18 21 24 27 30 33 36 39 1 5 10 14 19 23 28 32 37 41 7 13 20 2634 40 8 17 29 38 11 25 2 22 4 35 16 31错误:输入:10 3输出:6 8 7 1 3 4 2 9 5 102.概要设计(1)抽象数据类型的定义:为实现上述程序的功能,可以用整数存储用户的输入。
并将用户输入的值存储于线性表中。
线性表ADT定义如下:ADT list数据对象:整形数据关系:线性关系,即<ai,ai+1>(0≤a<n)。
基本操作:bool remove(int &elem)//移除一个元素,被移除的元素赋给elem//如果操作成功,返回true,否则返回falsebool isEmpty()//判断数组的元素是否清空,空返回true,否则返回falsebool setPos(int place)//设置当前元素的位置,设置成功返回true,否则返回falseint getLength()//获取数组的实际长度(2)算法的基本思想:约瑟夫环问题中的数据是人所在的位置,而这种数据是存在“第一元素、最后元素”,并且存在“唯一的前驱和后继的”,符合线性表的特点。
北邮数据结构实验一_约瑟夫问题_实验报告
北京邮电大学电信工程学院数据结构实验报告实验名称:实验一——线性表学生姓名:班级:班内序号:学号:日期:1.实验要求实验目的熟悉C++语言的基本编程方法,掌握集成编译环境的调试方法学习指针、模板类、异常处理的使用掌握线性表的操作的实现方法学习使用线性表解决实际问题的能力实验内容利用循环链表实现约瑟夫问题的求解。
约瑟夫问题如下:已知n个人(n>=1)围坐一圆桌周围,从1开始顺序编号。
从序号为1的人开始报数,顺时针数到m的那个人出列;他的下一个人又从1开始报数,数到m的那个人又出列;依此规则重复下去,直到所有人全部出列。
请问最后一个出列的人的编号。
2. 程序分析2.1 存储结构采用单循环链表实现约瑟夫问题的求解单循环链表存储结构示意图2.2 关键算法分析基本思想:首先,应该确定构造链表时所用的插入方法。
当数到m的那个人就出列,也即删除这个节点,同时建立这个节点的前节点与后节点的联系。
由于是循环计数,所以才采用循环列表方式。
第1页其次还要考虑输入值异常的情况。
之后,就是关于出列节点的逻辑判断。
依次找到待删结点的直接前驱,便于删除结点后修改它的直接后继,如此循环直到最后一个人出列。
关键算法:1.最后一个数据指向头指针,形成循环链表head=new Node; //确定头结点p=head;for(i=1;i<=n-1;i++) //赋初值{p->data=i;p->next=new Node; //为下一个新建内存p=p->next;}p->data=n; //最后一个单独处理p->next=head; //指向头,形成循环链表p=head;2.输入值异常的情况cout<<"请输入环内总人数n:";cin>>n;if (n<1) //考虑n输入错误的情况{cout<<"n值输入错误"<<endl;}cout<<"请输入起始人号码:";cin>>k;if (k<1||k>n) //考虑k输入异常的情况{cout<<"k值输入错误"<<endl;}cout<<"请输入m值:";cin>>m;if (m<1) //考虑m输入异常的情况{cout<<"m值输入错误"<<endl;}3.在约瑟夫环中实现逐个出环并找出最后一个出环的序号while(p!=p->next){for(i=1;i<m-1;i++) //查找q的节点p=p->next;q=p->next;cout<<"第"<<s<<"个出环的人编号是:"<<q->data<<endl;p->next=q->next;北京邮电大学信息与通信工程学院p=p->next;delete q; //释放q的空间s++;}cout<<"最后环内留下的人编号是:"<<p->data<<endl;delete p;算法步骤:①从第一个结点开始,查找第i-1个元素,设为p指向该结点;②设q指向第i个元素:q = p->next;,输出q元素的数据;③摘链,即将q元素从链表中摘除:p->next = q->next;p=p->next;delete q;3. 程序运行结果1、测试主函数流程:流程图如图所示第3页2、程序运行结果3、输入错误运行结果4. 总结在调试程序过程中,虽然没有编译错误,但是运行结果总是和准确值差1,最后发现是把i=1写成i=0,才造成的错误,改过之后就没有问题了。
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
HUNAN UNIVERSITY课程实习报告题目:约瑟夫问题学生姓名刘海龙学生学号201326010115专业班级软件1 3 0 1 指导老师李晓鸿完成日期 2 0 1 4 年11 月18 日一、需求分析1.输入的形式和输入值的范围本程序中,输入的人数n和报数值m均为整数,n、m均大于0,输入的形式为:n,m (n、m输入时用逗号隔开)2.程序功能提供用户从键盘输入约瑟夫环的关键数据,人数n和报数值m,并显示出列顺序。
3.输出的形式在DOS界面上输出这n个数的输出序列。
4.测试数据①输入(n,m均为正整数,且n>m)10,3输出3 6 9 2 7 1 8 5 10 4②输入(n,m均为正整数,且n<m)4,6输出2 1 4 3③输入(n,m均为正整数,且n=m)7,7输出7 1 3 6 2 4 5④输入 (n,m中有浮点数)8,5.56输出输入有误,请重新输入!⑤输入(n,m中有负数)-3,-8输出输入有误,请重新输入!⑥输入(n,m未按格式输入)aA2,3asf输出输入有误,请重新输入!二、概要设计抽象数据类型n(n为正整数)个人的编号为1~n,1相当于唯一的“第一元素”,n相当于唯一的“最后元素”,除最后元素n外,剩下的n-1个编号均有唯一的后继,除第一元素1外,剩下的n-1个编号均有唯一的前驱,符合线性结构,故应以线性表实现该结构。
ADT alist{数据对象:D={a i| a i∈int,i=1,2,…,n,n≥0}.数据关系:Rl={<a i-1,a i> | a i-1,a i∈D,i=2,…,n}基本操作:InitList(&L,size)//构造一个空线性表L。
Append(&L,e) //新元素e入表Remove(&L,i) //删除表中第i个元素,即将i之后的元素往前移一位。
DesList(&L)//销毁线性表,释放内存空间}无论选择哪种数据结构都应有一个结构初始化操作及相应的结构销毁操作,本程序的结构初始化操作为:InitList(&L,size),结构销毁操作为DesList(&L)。
在将n个人的编号存进线性表时,需要一个添加操作,故设计Append(&L,e),其用于向线性表表尾添加新元素e。
“出列”相当于删除线性表中某个指定的元素,这就需要一个删除操作,故设计Remove(&L,i),其根据下标索引需要删除的元素,“删除”即相当于将该元素之后的所有元素向前移动一位。
算法的基本思想n个人的编号为1,2,3,…,n(n>0),并且这n个人根据编号大小依序排列,即将这n个编号依序存入线性表中,报数值m为正整数。
①.编号为1的人从1开始依序报数,即从线性表的第一个元素起开始访问,并依序逐个访问它的下一个元素。
②.当轮到剩余人群中编号最大的人报数时,剩余人中编号最小的作为他的下一位,继续报数,即当线性表的表尾元素访问完后,将表头元素作为下一个访问对象。
③.数到m时报数停止,并且数到m的人出列,他的下一位从1开始重新报数。
即访问进行到第m次时停止,将第m次访问到的元素从表中删除,并在DOS界面上输出对应的编号,然后从它的下一个元素开始新一轮的访问(每轮m次)。
④.重复步骤2、3,直到出列n个人为止,即当表内元素均被删除为止,此时,整个出列序列已经输出在DOS界面上。
程序的流程程序由三个模块组成:(1)输入模块:完成人数和报数值的输入,存入变量n,m中。
(2)功能模块:设计一个实现约瑟夫问题的函数Joseph(&L,m),模拟循环报数,每出列一个元素,通过输出模块将其显示在屏幕上。
(3)输出模块:屏幕上显示出列的元素。
三、详细设计物理数据类型输入的人数n与报数值m均应为正整数,为了能够存储和处理,变量n,m采用C 语言中的int定义变量。
因为线性表是对n个人的编号1~n进行操作(编号1~n符合线性结构),过程中无需添加新的元素,即表内元素最多只有n个,为了减小空间上的开销,线性表采用顺序表来实现其物理结构。
线性表的每个元素存储的是n个人的编号1~n中的一个,所以线性表中元素类型定义为整型。
#define DefaultListSize 100 //线性表默认长度为100typedef int Elem;typedef int ElemType;typedef struct List{Elem* listArray; //存储空间基址int length; //当前长度int maxsize; //最大长度}Alist;void InitList(Alist &L, int size=DefaultListSize) //构造一个空线性表L{L.maxsize = size; //若未传参,size取默认值100L.listArray = new Elem[L.maxsize];L.length = 0;}void DesList(Alist &L) //销毁线性表,释放内存空间{delete[] L.listArray;}bool Append(Alist &L, ElemType e) //新元素e入表{若L.length等于L.maxsize,返回false,表满,无法添加L.listArray[L.length] = e;L.length++; //由于有新的元素入表,故表的长度应该加1返回true,表示新元素e入表成功}bool Remove(Alist &L,int i) //删除表中第i个元素,即将i之后的元素往前移一位。
{若L.length等于0,返回false,表空,没有需要删除的元素int j;for(j=i;j<L.length;j++) //从第i+1个元素(下标为i)起,后面的所有元素向前移一位{L.listArray[j - 1] = L.listArray[j];}L.length--; //由于从表中删除了一个元素,故表的长度应该减1返回true,表示删除成功}算法的具体步骤约瑟夫问题的实现(函数joseph(&L,m))算法流程图:删除(基本操作Remove(&L,i))的算法流程图:1.根据n的值新建一个长度为n的线性表,将编号1~n依次存入表中。
2.设变量t=0、i=L.length.3.若i>=1(即线性表中至少还有一个未出列的元素),则t=(t+m-1)%i(即应出列元素的下标)。
4.输出下标为t的元素。
5.将出列元素之后的所有元素往前移一位,实现元素的“删除”;之后,i自减一次(从表中删除了一个元素)。
6.重复步骤3、4、5,直到表中无剩余元素。
算法的时空分析算法的执行,主要是n个编号的出列,以及每次出列一个编号时,将其之后的编号向前移动一位(以达到“删除”目的),每次出列一个编号的时间代价为常量,记为c1,出列之后,将其之后的编号均前移一位的时间代价与n的大小有关,记为c2n,则整个算法执行的时间代价为:(c1+c2n)*n=c1*n+c2n2,即:Θ(n2)。
若采用循环链表实现线性表,则删除的时间代价仅为常数级,相应的算法代价只要Θ(n)。
函数的调用关系图输入和输出的格式输入本程序用于解决约瑟夫环问题。
//提示请输入人数n和报数值m,用逗号隔开,如“10,3”://提示等待输入输出出列序列为://提示//输出结果的位置,每个编号之间间隔两个空格长度例如:输入本程序用于解决约瑟夫环问题。
请输入人数n和报数值m,用逗号隔开,如“10,3”:12,6输出出列序列为:6 127 2 108 59 1 11 4 3四、调试分析本题调试的难点主要是对输入合法性的验证。
输入的格式被设计为:“n,m”。
要求先输入一个正整数n,再输入一个逗号,继续输入正整数m,最后按回车结束。
其他形式的输入均不合法。
scanf()函数的格式为:scanf_s("%f,%f", &n, &m)。
scanf()函数按格式正确读入的返回值为2。
初步设计时,根据scanf()函数的返回值,以及成功读入后对n、m的验证(判断n、m是否为负数或浮点数)已能验证需求分析时设计的所有样例。
但是当输入格式为“1,2abc”、“4,21sf”的格式时(即整个输入的前部分是正确格式,如“1,2”、“4,21”),程序会有根据前部分正确格式计算得到的输出(如输入“1,2abc”时,输出“1”;输入“4,21”时,输出“1 4 2 3”)。
此时的合法性验证中,只要输入时前部分的正确格式被scanf()读取,就会直接无视后半部分不合法的输入,这显然是不合理。
为了避免上述情况,我重新设计了输入合法性验证模块。
通过查阅资料了解到,scanf()成功按格式读入后,输入的回车‘\n’仍余留在输入缓冲区中。
根据这一特点,我首先通过getchar()函数来判断输入缓冲区的余留的第一个字符是否为回车‘\n’,若不是,则证明输入不合法,且该种不合法情况为有多余字符或字符串的输入,这就包括了上述形如“1,2abc”、“4,21sf”的情况,初始值为0的计数变量count在这种情况下加1,在循环的验证中根据count的值来排除这种情况;若是,则通过检验scanf()的返回值和n、m是否为负数或浮点数来验证其合法性。
这样改进后就在原有案例能成功验证的基础上成功规避了上述不合理情况。
之后,又设计了多组测试数据,均能正确验证。
改进前的输入及合法性验证模块:void input(ElemType &n1, ElemType &m1) //输入及合法性检查{float n2, m2;int jud;do{jud = scanf_s("%f,%f", &n2, &m2);if ((jud != 2) || (n2 <= 0) || (m2 <= 0) || (n2 - int(n2)) != 0 || (m2 - int(m2)) != 0){printf("输入有误,请重新输入!\n");while (getchar() != '\n'){}; /*清空输入缓冲,可能有字符串输入,所以用了循环*/}} while ((jud != 2) || (n2 <= 0) || (m2 <= 0) || (n2 - int(n2)) != 0 || (m2 - int(m2)) != 0 );n1 = int(n2);m1 = int(m2);}改进后的输入及合法性验证模块:void input(ElemType &n1, ElemType &m1) //输入及合法性检查{float n2, m2;int jud, count = 0;do{count = 0;jud = scanf_s("%f,%f", &n2, &m2); //按格式成功读入返回2if (getchar() == '\n') //scanf成功读取后仍会余留回车符{if ((jud != 2) || (n2 <= 0) || (m2 <= 0) || (n2 - int(n2)) != 0 || (m2 - int(m2)) != 0){printf("输入有误,请重新输入!\n");continue;}}else{printf("输入有误,请重新输入!\n");count++;while (getchar() != '\n'){}; //清空输入缓冲,可能有字符串输入,所以用了循环}} while ((jud != 2) || (n2 <= 0) || (m2 <= 0) || (n2 - int(n2)) != 0 || (m2 - int(m2)) != 0 || count != 0);n1 = int(n2);m1 = int(m2);}改进前:输入(前部分为正确格式)3,5sd输出2,3,1截图:改进后:输入(前部分为正确格式)3,5sd输出输入有误,请重新输入!截图:五、测试结果①输入(n,m均为正整数,且n>m)10,3输出3 6 9 2 7 1 8 5 10 4测试截图:②输入(n,m均为正整数,且n<m)4,6输出2 1 4 3测试截图:③输入(n,m均为正整数,且n=m)7,7输出7 1 3 6 2 4 5测试截图:④输入 (n,m中有浮点数)8,5.56输出输入有误,请重新输入!测试截图:⑤输入(n,m中有负数)-3,-8输出输入有误,请重新输入!测试截图:⑥输入(n,m未按格式输入)aA2,3asf输出输入有误,请重新输入!测试截图:六、用户使用说明1、本程序的运行环境为Win7 64位操作系统,执行文件为Exp1josephus.exe2、运行程序时提示输入人数和报数值,注意人数和报数值均应为正整数,输入不合法时要求重新输入,在输入时,两者用逗号隔开。