大数据结构实验一约瑟夫问的题目

合集下载

数据结构-约瑟夫实验

数据结构-约瑟夫实验

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)——约瑟夫问题

数据结构与算法分析(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(约瑟夫)问题

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)问题

数据结构实验一约瑟夫(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. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 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、运行程序时提示输入人数和报数值,注意人数和报数值均应为正整数,输入不合法时要求重新输入,在输入时,两者用逗号隔开。

相关文档
最新文档