约瑟夫环实验报告
数据结构 课程设计报告
题 目:约瑟夫环实验 班 级: 成 员: 指导教师: 完成日期: 第 1 页 共 24 页 1
目录: 实习报告要求................................................................ 2 约瑟夫环实验描述 ........................................................ 3 课程设计报告正文 ................................. 4 一.需求分析................................................................ 4 二.概要设计................................................................ 4 三.详细设计................................................................ 6 1.各模块关键代码及算法的解释: ............. 6 2.函数的调用关系图 ......................... 8 四.调试分析.............................................................. 11 五.用户使用说明 ...................................................... 12 六.测试结果.............................................................. 13 七.小结 ....................................................................... 3 八.附录,带注释的源程序 .......................................... 4 第 2 页 共 24 页 2
实习报告要求 实习报告的开头应给出题目、班级、姓名、学号和完成日期,并包括以下七个内容: 1. 需求分析 以无歧义的陈述说明程序设计的任务,强调的是程序要做什么?明确规定: (1) 输入的形式和输入值的范围; (2) 输出的形式; (3) 程序所能达到的功能; (4) 测试数据:包括正确的输入及其输出结果和含有错误的输入及其输出结果。 2. 概要设计 说明本程序中用到的所有抽象数据类型的定义、主程序的流程以及各程序模块之间的层次(调用)关系。 3. 详细设计 实现概要设计中定义的所有数据类型,对每个操作只需要写出伪码算法;对主程序和其他模块也都需要写出伪码算法(伪码算法达到的详细程度建议为:按照伪码算法可以在计算机键盘直接输入高级程序设计语言程序);画出函数的调用关系图。 4. 调试分析 内容包括: (1)调试过程中遇到的问题是如何解决的以及对设计与实现的回顾讨论和分析; (2)算法的时空分析(包括基本操作和其他算法的时间复杂度和空间复杂度的分析)和改进设想; (3)经验和体会等。 5. 用户使用说明 说明如何使用你编写的程序,详细列出每一步的操作步骤。 6. 测试结果 列出你的测试结果,包括输入和输出。这里的测试数据应该完整和严格,最好多于需求分析中所列。 7. 附录 带注释的源程序。可以只列出程序文件名的清单。
实习报告的各种文档资料,如:上述中的前三部分要在程序开发的过程中逐渐充实形成,而不是最后补写(当然也可以应该最后用实验报告纸誉清或打印)。 第 3 页 共 24 页 3
约瑟夫环实验描述 【问题描述】 约瑟夫 (Joseph) 问题的一种描述是:编号为 1,2,… ,n 的n个人按顺时针方向围坐一 圈,每人持有一个密码(正整数)。一开始任选一个正整数作为报数上限值m,从第一个人开始按顺时针方向自1开始顺序报数,报到m时停止报数。报m的人出列,将他的密码作为新的m值,从他在顺时针方向上的下一个人开始重新从1报数,如此下去,直至所有人全部出列为止。试设计一个程序求出出列顺序。 【基本要求】 利用单向循环链表存储结构模拟此过程,按照出列的顺序印出各人的编号。 【测试数据】 m 的初值为 20;n=7,7 个人的密码依次为:3,1,7,2,4,8,4, 首先 m 值为 6( 正确的出列顺序应为 6,1,4,7,2,3,5) 。 【实现提示】 程序运行后,首先要求用户指定初始报数上限值,然后读取各人的密码。可设n≤30。 此题所用的循环链表中不需要 “头结点”,请注意空表和非空表的界限。 【选作内容】 向上述程序中添加在顺序结构上实现的部分。 第 4 页 共 24 页 4
课程设计报告正文 一.需求分析 1.输入的形式和输入值的范围 本程序中,输入人数n(n小于等于30)和初始密码m(m大于等于1),然后给每个人输入所持有的密码key,均限定为正整数。用单循环链表模拟约瑟夫环,从队头开始从1报数,报到m的人出列,再把此人的密码赋给m,继续下一轮的报数,直到所有的人出列。结果出来后再选择输入一数字,输入1继续新一轮的游戏,输入0结束,退出此游戏。演示程序以用户和计算机对话方式进行,根据提示信息由用户从键盘输入数据,输入的形式为一个以“回车符”为结束标志的正整数。 2.输出的形式 在计算机终端上显示提示信息之后,有用户在键盘上输入演示程序中规定的运算命令,相应的输入数据和运算结果显示在其后。运行后输出的结果是约瑟夫环的相关信息和经过报数后出队的人的序列号及相关信息。 程序执行的命令包括:(1)输入人数和初始密码 (2)输入所有人的密码 (3)显示输入的所有人的编号及相应的密码(4)输出出列密码及编号 (5)结束 即:(1)构造单循环链表;(2)输出循环链表的信息;(3)按照出列的顺序输出各人的编号 3.程序功能 提供用户从键盘输入,Joseph约瑟夫环的必要数据,并从屏幕显示出列顺序。 4.测试数据 (1)n=7 ,m=20(常规数据), 7个人的密码依次为3,1,7,2,4,8,4,正确的输出序列为6,1,4,7,2,3,5. (2)n=5,m=6(常规数据),5个人的密码依次为2,5,4,6,7,正确的输出序列为1,3,4,2,5。 (3)n=31, m=3(错误数据),“这个人数太大了,请重新输入”。 (4)n=0, m=3(错误数据),“这个约瑟夫环里没有人!”。 (5)n=1, m=4,(边缘数据),正确的输出序列为1。 (6)n=3, m=1,(边缘数据),正确的输出序列为1,3,2。 第一组为常规数据,中间两组为错误数据,后面两组为边缘数据。 5.设计思路及方案 用单循环链表来模拟约瑟夫环,结点信息包括编号、密码、指向下一个结点的指针,用三大主要的函数来实现功能,包括创建链表的函数、输出链表信息的函数、删除结点也就是出队的函数、主函数等函数。
二.概要设计 1.抽象数据类型的定义 第 5 页 共 24 页 5
为实现上述功能,应以有序单向循环链表表示约瑟夫环。为此,需要有一个抽象数据类型。该抽象数据类型的定义为: ADT LinkList { 数据对象:D={ ai | ai ∈termset,i=1,2,„„n,n>=0}, termset中每个元素包含编号,密码,和一个指向下一节点的指针 数据关系:R1={ | ai-1, ai ∈D , i=2,„„n} 基本操作: LinkList EvaluList(int n);//对单向循环链表进行尾插入赋值 int size(LinkList L);//求链表的节点个数 Status ScanList(LinkList L);//遍历单向循环链表 Status Joseph(LinkList &L,int m);//约瑟夫环的实现 } 此抽象数据类型中的一些常量如下:#define TRUE 1 #define FALSE 0 #define OK 1 typedef int Status; typedef double ElemType; 单向循环链表中节点的定义如下所示: typedef struct LNode { int number; int data; struct LNode *next; }LNode, *LinkList; 2.主程序的流程 void main() { 初始化; 输入数据; 执行功能; 显示结果; } 3.模块的设计及介绍 (1)主程序模块 Void main(){ 初始化; do { 接受命令; 处理命令; }while(“命令”=“退出”); } (2)创建链表模块 Static void creatlist(行参){
实验一 约瑟夫环问题
实验一约瑟夫环问题约瑟夫环问题:设编号为1,2,3,……,n的n(n>0)个人按顺时针方向围坐一圈,每个人持有一个正整数密码。
开始时任选一个正整数做为报数上限m,从第一个人开始顺时针方向自1起顺序报数,报到m是停止报数,报m的人出列,将他的密码作为新的m值,从他的下一个人开始重新从1报数。
如此下去,直到所有人全部出列为止。
令n最大值取30。
要求设计一个程序模拟此过程,求出出列编号序列。
实现:用一个不带头结点的单向循环链表表示上述约瑟夫环,结点结构可定义为:typedef sturct node Array { int no; /*编号*/int key; /*密码域*/struct node *next; /*指针域*/} jNode;注意:不带头结点的单向循环链表判空条件的处理。
#include<stdio.h>#include<stdlib.h>typedef struct Joseph{int num;int key;struct Joseph *next;} Joseph1;Joseph1 *CreatList(int n){Joseph1 *R,*p,*q;int i,k;R=p=(Joseph1*)malloc(sizeof(Joseph1));p->next=NULL;for(i=0;i<n-1;i++){q=(Joseph1*)malloc(sizeof(Joseph1));p->num=i+1;scanf("%d",&k);if(k<=0){printf("输入信息有误!");exit(0);}p->key=k;p->next=q;p=q;}q->num=n;scanf("%d",&k);if(k<=0){printf("输入信息有误!");exit(0);}q->key=k;q->next=R;R=q;return(R);}void DeleList(int n,Joseph1 *P,int m){Joseph1 *q,*t;q=P;int i,j;for(i=1;i<n;i++){for(j=1;j<m;j++)q=q->next;t=q->next;q->next=t->next;m=t->key;printf("删除的第%d个数是:",i);printf("%d\n",t->num);free(t);}printf("删除的最后一个数是:%d\n",q->num);free(q);}void main(){int m,n;Joseph1 *P;printf("请输入参加的人数: ");scanf("%d",&n);if(n<=0|){printf("输入信息有误!");exit(0);}printf("请输入初始密码: ");scanf("%d",&m);if(m<=0){printf("输入信息有误!");exit(0);}printf("请输入每个人的密码: ");P=CreatList(n);DeleList(n,P,m);}。
约瑟夫问题实验报告
X X X X X X X X实验报告课程:数据结构班级:姓名:学号:成绩:指导教师:实验日期:2012.3实验密级:预习程度:实验时间:仪器组次:必修/选修:必修实验序号:01实验名称:线性结构的应用——约瑟夫问题实验目的与要求:编程实现约瑟夫问题。
假设有n个人围圈而坐,现在从第k人开始数数,数到m的人出列,紧接着的后续人又从1开始数数,到m出列,如此重复下去,直到全体人员出列。
编程实现新出列的数字序号输出。
实验仪器:名称型号数量微机1一、概要设计本程序使用了循环链表这一数据结构。
循环链表在普通链表的基础上,将最后一个节点的指针域指向头结点。
具体定义为:typedef struct CLNode{ int data;struct CLNode *next;}Node;//定义单向循环链表结构体,并令最后一个指针指向头结点流程图如下:注:在*处调用子函数为head 分配存储空间,如果内存不够,结束程序 head=head->next; head->data=i; i++; head==NULL? i<=n? 为p 分配存储空间,如果内存不够,结束程序 pr->next=p;p->data=i; p->next=head; pr=p; 开始 从主函数中接受头结点指针head ,n i=1,Node *p=NULL , Node *pr=head; 返回head 是是否 否图2——子函数:建立n 阶循环链表,返回链表的头指针图1——主函数开始 输入总人数(n)、从第几个人开始数(k)、数到第几个人出列(m) 构造n 项循环链表*执行”head=head->next ”k 次,修改头指针指向Node *head=NULL;int i=0; 按顺序输出? 否 是是否 结束释放链表剩余存储空间 输出head->data 实现报数并出列* head!=head->ne xt?直接按顺序输出开始从主函数中接受head和mint i=1;Node *p=head;Node *pre=NULL;执行:”pre=p;p=p->next;”m次,模拟报数并移动指针此时,p->data便是出列编号,输出其值删除p指向的节点,令p之后的节点为head返回head图3——子函数:当链表中的结点个数大于1,调用此函数,实现目标出列,并删除该节点,另该节点下一结点为头结点,返回修改后的头结点二、详细设计本程序核心算法主要有两部分:一是建立循环链表,二是实现目标人员的出列。
大学数据结构课程设计-实习报告——约瑟夫环
20XX年复习资料大学复习资料专业:班级:科目老师:日期:数据结构实习报告题目:程序求解约瑟夫环问题出列顺序一、需求分析1.以单项循环链表存储结构模拟约瑟夫环问题。
即编号为1、2、3…、n的n个人按顺时针方向围坐一圈,每人持有一个密码(正整数)。
一开始任选一个正整数作为报数上限值m,从第一个人开始按顺时针方向自1开始报数,报到m时停止报数。
报m的人出列,将他的密码作为新的m值,从他在顺时针方向下一个人开始重新从1报数,如此下去,直至所有的人全部出列为止。
按出列顺序印出各人的编号。
2.演示程序以用户与计算机的对话方式执行,用户输入相应的数据,输出结果显示在其后。
3.测试数据:(1)n=77个人的密码依次为:3,1,7,2,4,8,4;首先m值为6(正确的输出顺序为:6,1,4,7,2,3,5)(2)n=55个人的密码依次为:1,2,3,4,5首先m值为1(正确的输出顺序为:1,2,4,3,5)(3)n=5首先m值为-2(输入的人数和初始密码不能为负数!)二、概要设计为实现上述程序功能,应利用单向循环链表存储结构模拟此过程。
1.单向循环链表的抽象数据类型定义为:ADT CircularList{数据对象:D={ai|ai∈LNode,i=1,2,…,n,n≥0}数据关系:R1={<ai-1,ai>|ai-1∈D,i=2,…,n}基本操作:Status ListDelete_L(LinkList &L,int I,ElemType &e)操作结果:在带头结点的单链表L中,删除第i个元素,并用e返回其值}2.本程序中包括的三个基本模块:1)主程序模块:Void main(){初始化;do{接受命令;处理命令;}while(“命令”=”退出”)}2)线性表模块:实现线性表的抽象数据结构3)元素结构单元模块:定义线性表中每个元素的结构三、详细设计1.结点类型typedef struct LNode{int data;//密码int i;//编号struct LNode *next;}LNode, *LinkList;2.用循环链表存储约瑟夫环,没有头结点,基本操作函数如下:int CreateList(LinkList &L,int a[],int n){//创建循环链表结构的约瑟夫环LinkList s,r;int i;r=L;for(i=1;i<=n;i++){s=(LinkList )malloc(sizeof(LNode));s->data=a[i];s->i=i;if(i==1){L=s;s->next=s;r=s;//尾指针指向当前元素}else{s->next=r->next;r->next=s;r=s;}}return 1;}int ListDelete_L(LNode *L){//删除表中一个结点if(L->next==L)//只剩一个结点{printf("%d\n",L->i);free(L);return 0;}LNode *p;p=L->next;//p指向要删除元素的下一个结点while(p->next!=L)p=p->next;LNode *q=L;//q指向需要被删除的元素结点int e=L->i;p->next=L->next;free(q);printf("%d ",e);return 1;}int FindNode(LinkList L,int x){//查找表中某个结点LinkList p=L;LinkList q;for(int i=1;i<x;i++)p=p->next;q=p->next;//下一次循环的起始位置x=p->data;if(ListDelete_L(p))FindNode(q,x);return p->i;}3.主函数:int main(){int n,m;LinkList L;printf("请输入人数和初始密码:");scanf("%d %d",&n,&m);if(n<0||m<0){printf("输入的人数和初始密码不能为负数!\n");return 0;}int a[20XXXX0];printf("请输入每个人的密码:");for(int i=1;i<=n;i++)scanf("%d",&a[i]);if(CreateList(L,a,n)){printf("\n");printf("正确的出列顺序为:");FindNode(L,m);printf("\n");}return 0;}4.函数的调用关系图反映了演示程序的层次结构:CreateList FindNodeListDelete_L四、调试分析1.刚开始时,忽略了题目要求的没有头结点这一项,没有把第一个结点单独拿出来建立,出现了逻辑上的错误。
DS实验报告
《算法与数据结构》实验报告实验一约瑟夫环问题(2学时)将编号为1,2,…,n的n个人按顺时针方向围坐一圈,每人持有一个密码(正整数)。
一开始任选一个正整数作为报数上限值m,从第一个开始按顺时针方向自1开始报数,报到m时停止报数。
报m的人出列,从他在顺时针方向上的下一个人开始重新从1报数,如此下去,直至所有人全部出列为止。
试设计一个程序求出出列顺序。
实验二链表的操作(4学时)编一程序:①建立一个数据域为1至10的带头结点的链表;②将此链表就地逆转。
链表的类型说明如下:#include <iostream.h>#include <stdlib.h>class Node{public:int data;Node *next;Node( ){next=0;}friend class LinkList;};//Nodeclass LinkList {Node *head;public:LinkList( ) { head=new Node(); }void Create(); //创建长度为n的单链表int GetElem(int i); //返回第i个元素值Node* Locate(int e); //返回第一个与e匹配的元素结点指针bool IsEmpty( ) {return (head->next==0);} //判断是否为空链表void Insert(int x, int i ); //在第i个结点之前插入元素值为x的结点int Delete ( int i ); //删除第i个结点,并返回其元素值void Clear( ); //清空链表void print( );};// LinkList实验三栈的应用-中缀表达式的求值(4学时)表达式求值是计算机实现程序设计语言中的基本问题之一,也是栈应用的一个典型例子,通过本实验,对输入的一个表达式进行求值。
说明:表达式中只包含+、-、×、/ 运算及(和)。
C实验报告约瑟夫斯环问题
约瑟夫斯(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)。
4.简述每一部分的对象、目的和要求:(1)主函数部分:对象:链表;目的:创建新链表;要求:将该链表初始化。
(2)被调函数部分:对象:结构体;目的:创建循环链表并且模拟约瑟夫斯问题;要求:让链表首尾相接,并且释放原来单链表的头结点空间。
通过该函数的调用,依次输出各结点的编号,并且符合规则。
二.需求分析1.程序所能达到的基本可能:该程序用循环链表来模拟n个人顺时针围坐一圈,用链表中的每一个结点代表一个人,结点结构体中设置两个变量,分别存放其编号以及密码。
在输入初始密码后,对该链表进行遍历,直到遍历到第m个结点,释放其空间,使其出列。
然后用被释放空间结点的密码值作为新的密码值,重复上述过程,直至所有结点被释放空间出列,链表成为空链表。
2.输入输出形式及输入值范围:程序运行后显示提示信息:"Please input a data:",提示用户输入密码信息,在输入达预定次数后自动跳出该循环,同时循环链表创建成功,在主函数中有相应的输出给予检验。
然后程序显示提示信息:"Please input the number m:",提示用户输入初始密码,程序执行结束后会输出相应的出列结点的顺序,亦即其编号。
Joseph ring实习报告
约瑟夫环问题模拟一、需求分析1、本演示程序模拟约瑟夫环问题:编号为1,2,……,n得n个人按顺时针方向围坐一圈,每人持有一个密码(正整数)。
一开始任选一个正整数作为报数上限值m,从第一个人开始按顺时针方向自1开始顺序报数,报到m时停止报数。
报m的人出列,将他的密码作为新的m值,从他在顺时针方向上的下一个人开始重新从1报数,如此下去,直至所有人全部出列为止。
2、演示程序以用户和计算机对话的方式执行,即在计算机终端上显示“提示信息”之后,由用户在键盘上输入演示程序中需要的相关信息及命令。
运算结果显示在其后。
3、程序需要输入的信息与命令包括:1)初始人数n ;2)n 个人的密码;3)初始的m 值;4、测试数据1)n:7 密码:3、1、7、2、4、8、4 m:62)n:1 密码:4 m:33)n:4 密码:4、2、3、1 m:124)n:3 密码:1、2、3 m:2二、概要设计为了实现上述程序功能,应以单向循环链表模拟问题过程。
每个结点代表一个玩家。
1、有序表抽象数据类型定义:ADT list{数据对象:D = {a i | a i ∈LNode, i = 1,2,……,n, n >= 0}数据关系:R = {<a i-1, a i> | a i-1, a i∈D, i = 2,……,n}基本操作:Init(&L)操作结果:构造一个空的单向循环链表。
Insert(&L, n, s)初始条件:链表L已存在,玩家编号n,玩家密码s。
操作结果:插入一个玩家结点。
Del(&L, m, e)初始条件:链表L已存在,m值有效。
操作结果:删除链表中第m个结点,并有e返回这个结点。
}ADT list2、本程序包含两个模块:a)主程序模块:int main(){初始化;接受命令;处理命令;退出;return 0;}b)循环单向链表单元模块——实现单向循环链表抽象数据类型;模块之间的调用关系如下:主程序模块↓单向循环链表单元模块三、详细设计int main() {//主函数main的实现int n, m, s, i;List l;if (Init(l)) { //初始化链表,执行成功后接受命令printf("how many players? : ");scanf("%d", &n);for (i = 1; i <= n; i++) {printf("the password of the number %d player: ", i);scanf("%d", &s);if (!Insert(l, i, s)) {printf("ERROR!");exit(1);}}//forprintf("Input the value of m: ");scanf("%d", &m);Lnode e;for (i = n; i >= 1; i--) { //处理命令,模拟人员的退出——结点的删除if (Del(l, m, e)) {printf("the number %d player come out\n", e.intNum);m = (m - 1 + e.intSec) % (i - 1);if (m == 0) m = i - 1;}}//for}//ifelse printf("ERROR!");return 0;}typedef struct Lnode {//链表结点类型定义int intNum;int intSec;struct Lnode *next;}Lnode, *Linklist;typedef struct {//循环单向链表类型定义Linklist head;Linklist tail;}List;bool Init(List &l) {//链表初始化l.head = l.tail = NULL;return true;}bool Insert(List &l, int N, int S) {//在链表末尾插入玩家号为N,玩家密码为S的结点Linklist p = (Linklist)malloc(sizeof(Lnode));if (!p) return false;p ->intNum = N;p ->intSec = S;if (!l.head) {l.head = p;}else {l.tail ->next = p;}p ->next = l.head;l.tail = p;return true;}bool Del(List &l, int m, Lnode &e) {//删除链表中第m个结点,并用e返回Linklist p = l.tail;if (!p || m < 1) return false;int cnt = 1;while (cnt < m) {p = p ->next;cnt++;}if (l.head == l.tail) {p ->next = NULL;e = *p;free(p);return true;}Linklist q = p ->next;if (q == l.tail) l.tail = p;if (m == 1) l.head = q ->next;p ->next = q ->next;q ->next = NULL;e = *q;free(q);return true;}四、调试分析1、由于程序时模拟约瑟夫环过程的,只是单纯的对问题的抽象解决,在数据范围不是很大的情况下程序可以比较迅速的完成问题的模拟,但是在大数据范围下,本程序模拟的方法是不适用的,需要更优化的算法。
实验一:约瑟夫问题
实验一:约瑟夫问题问题描述:用数组和链表存储方式实现约瑟夫问题。
约瑟夫问题: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
数据结构上级实验报告1——采用单向环表实现约瑟夫环一实验目的采用单向环表实现约瑟夫环二实验内容请按以下要求编程实现:①从键盘输入整数m,通过create函数生成一个具有m个结点的单向环表。
环表中的结点编号依次为1,2,……,m。
②从键盘输入整数s(1<=s<=m)和n,从环表的第s个结点开始计数为1,当计数到第n个结点时,输出该第n结点对应的编号,将该结点从环表中消除,从输出结点的下一个结点开始重新计数到n,这样,不断进行计数,不断进行输出,直到输出了这个环表的全部结点为止。
例如,m=10,s=3,n=4。
则输出序列为:6,10,4,9,5,2,1,3,8,7。
三程序设计(1)概要设计抽象数据类型:struct node{ int number;struct node * next;};宏:无主函数流程与各函数模块调用关系:创建头结点=〉输入链表的长度m=〉调用create函数建立链表=〉输入首次定位的节点s 以及间隔数n=〉调用deal函数处理链表=〉结束函数模块间无彼此调用关系(2)详细设计建立链表算法实现:void create(node * head,int m){node * p,*q;int i = 1;for(;m>0;m--){ p = (node *)malloc(sizeof(node));if(p == NULL) exit(0);p->number = m;p->next = head->next;head->next = p;if(i){q = p ; i--;}}q->next = head->next;}处理链表算法实现:void deal(node * head,int m,int s,int n){node * p,*q;int count=1,tag=0;p = head;while(s--){ p = p->next;}while(tag != m){if(count == n){printf("%d->",p->number);for(q = p->next; q->next != p; q=q->next);q->next = p->next;p = p->next;q = q->next;count = 1;tag++;}else {p = p->next;count++;}}printf("<-end\n");}主函数代码实现:int main(){ int m,s,n;node * head, * p;p = (node *)malloc(sizeof(node));p->next == NULL;head = p;printf("please input number m:\n");scanf("%d",&m);create(head,m);printf("please input number s(1<=s<=m):\n");scanf("%d",&s);printf("please input number n:\n");scanf("%d",&n);deal(head,m,s,n);system("pause");}四程序调试分析1在deal函数中进行对p节点的删除操作时出现问题,定位p的前一个节点q时,依旧采用了head指针从头遍历的方法,但是忽略了在函数操作中会删除掉头结点,这样head指针指向位置改变不能得到正确结果。
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号。
2.1约瑟夫环问题
问题描述
设编号为1、2、……n的n个人围坐一圈, 约定编号为k(1<=k<=n)的人从1开始报数, 数到m的那个人出列,它的下一位又从1开始 报数,数到m的那个人又出列,依次类推,直 到所有人出列为止,由此产生一个出队编号的 序列。
数据结构设计
运行与测试
1、当m的初值为5,n的初值为7,从第1个人开始 数,则正确的出列顺序应为5、3、2、4、7、1、 6,经程序运行测试,结果正确。 2、程序容错性的测试,当输入k的值不符合问 题约定时候应有错误提示给用户,指导用户正 确输入,并作出相应处理保证程序健康的运行。
Hale Waihona Puke 位又从开始数据结构设计由于当某个人退出圆圈后报数的工作要从下一个人开始继续剩下的人仍然是围成一个圆圈的可以使用循环表由于退出圆圈的工作对应着表中结点的删除操作对于这种删除操作频繁的情况选用效率较高的链表结构除操作频繁的情况选用效率较高的链表结构为了程序指针每一次都指向一个具体的代表一个人的结点而不需要判断链表不带头结点
由于当某个人退出圆圈后,报数的工作要 从下一个人开始继续,剩下的人仍然是围成一 个圆圈的,可以使用循环表,由于退出圆圈的 工作对应着表中结点的删除操作,对于这种删 除操作频繁的情况,选用效率较高的链表结构, 为了程序指针每一次都指向一个具体的代表一 个人的结点而不需要判断,链表不带头结点。 所以,对于所有人围成的圆圈所对应的数据结 构采用一个不带头结点的循环链表来描述。设 头指针为p,并根据具体情况移动。
可以采用数据类型定义: typedef struct node { int number;//每个人的编号 struct node *next;//指向表示下一个人的结点 的指针 }Lnode,*Linklist; Linklist p;
