数据结构实验一约瑟夫问题
约瑟夫问题
![约瑟夫问题](https://img.taocdn.com/s3/m/179cb419a8114431b90dd896.png)
一问题描述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的值再输入每个人的密码最后得到出列顺序八附录(源程序)这里附上两种源程序,本质上相同,只是第一个程序按老师要求写为很多子函数形式,第二个是我已开始编的,一个大函数。
实验报告——约瑟夫环
![实验报告——约瑟夫环](https://img.taocdn.com/s3/m/bc916d5f77232f60dccca102.png)
《数据结构》课程设计报告课程名称:《数据结构》课程设计课程设计题目:约瑟夫环姓名:张光栋院系:计算机学院专业:网络工程年级:2013级学号:13055532指导教师:张纪林一、需求分析1.以单项循环链表存储结构模拟约瑟夫环问题。
即编号为1、2、3…、n的n 个人按顺时针方向围坐一圈,每人持有一个密码(正整数)。
一开始任选一个正整数作为报数上限值m,从第一个人开始按顺时针方向自1开始报数,报到m时停止报数。
报m的人出列,将他的密码作为新的m值,从他在顺时针方向下一个人开始重新从1报数,如此下去,直至所有的人全部出列为止。
按出列顺序印出各人的编号。
2.演示程序以用户与计算机的对话方式执行,用户输入相应的数据,输出结果显示在其后。
3.测试数据:(1)n=55个人的密码依次为:2,4,2,6,2;首先m值为2(正确的输出顺序为:2 1 4 3 5)(2)n=77个人的密码依次为:2,4,1,4,3,2,3首先m值为5(正确的输出顺序为:5 1 3 4 6 2 7)二、概要设计为实现上述程序功能,可利用单向循环链表存储结构模拟此过程。
1.单向循环链表的抽象数据类型定义为:ADT CircularList{数据对象:D={ai|ai∈LNode,i=1,2,…,n,n≥0}数据关系:R1={<ai-1,ai>|ai-1∈D,i=2,…,n}基本操作:Status LisCreate_L(LinkList &L,int I,ElemType &e)操作结果:在不带头结点的单链表L中,创建第i个元素,并用e赋值}2.本程序中包括的两个基本模块:1)主程序模块:Void main(){初始化;do{接受命令;处理命令;}while(“命令”=”退出”)}2)循环链表模块:实现循环链表的抽象数据结构三、详细设计1.结点类型typedef struct ListNode{int mi;int n;struct ListNode *next;}ListNode,*LinkList;2.用循环链表存储约瑟夫环,没有头结点,基本操作函数如下:void CreateList(LinkList&L, int n){LinkList s;int i;L=(LinkList)malloc(sizeof(ListNode));L->n=1;L->next=L;for(i=2;i<=n;i++){s=(LinkList)malloc(sizeof(ListNode));s->next=L->next;L->next=s;s->n=i;L=L->next;}}void Delete(LinkList L, int m){int i;LinkList p,q;p=L;while(p->next!=p){for(i=1;i<m;i++)p=p->next;q=p->next;m=q->mi;printf("%d ",q->n);p->next=q->next;free(q);}printf("%d ",p->n);free(p);}3.主函数:int main(){int n,i,m;LinkList L,p;printf("请输入人数:");scanf("%d",&n);CreateList(L,n);printf("请输入密令\n");p=L->next;for(i=1;i<=n;i++){printf("请输入第%d条密令\n",i);scanf("%d",&p->mi);p=p->next;}printf("请输入初始密令\n");scanf("%d",&m);printf("输出为\n");Delete(L, m);return 0;}四、调试分析1.第一次写时,没有区分出只剩下的一个的情况,导致最终输出出现错误。
实验一:约瑟夫问题
![实验一:约瑟夫问题](https://img.taocdn.com/s3/m/38a698641ed9ad51f01df296.png)
实验一:约瑟夫问题问题描述:用数组和链表存储方式实现约瑟夫问题。
约瑟夫问题: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. 通过上机实践,掌握了用高级语言实现算法的基本步骤和方法.(最前面加班级、学号、姓名)。
约瑟夫问题的链式求解法
![约瑟夫问题的链式求解法](https://img.taocdn.com/s3/m/429bfd0df78a6529647d5363.png)
实验一约瑟夫问题求解一、实验目的1、掌握上机调试线性表的基本方法;2、掌握线性表的基本操作:插入、删除、查找等运算在顺序存储结构和链式存储结构上的运算。
3、掌握约瑟夫问题求解算法二、实验内容1、认真阅读和掌握本实验的参考程序,并上机运行本程序,可尝试完善删除、查找等运算。
2、参考第二章课件中对瑟夫问题求解算法的描述,选择一种存储结构实现该问题的求解。
三、注意事项:1、在磁盘上创建一个目录,专门用于存储数据结构实验的程序,可通过U盘或邮箱长期保存程序。
2、实验报告要求:(1)算法的完整代码;(2)程序运行结果及分析;(3)实验总结。
四、参考程序1、顺序表的插入(1)程序代码#include "stdio.h"#include "stdlib.h"typedef struct SeqList{int data[100];int length; /* 表长*/}SeqList , *PSeqList;PSeqList creaeNullList_seq()/* 创建空线性表*/{PSeqList palist=(PSeqList)malloc(sizeof(struct SeqList));if(palist!=NULL){palist->length=0;return(palist);}printf("Out of space!!\n");return NULL;}int isNullList_seq(PSeqList palist)/* 判断是否空线性表*/{return (palist->length==0);}int insertPre_seq(PSeqList palist,int p,int x){int q;if(palist->length>=100){printf("overflow!\n");return(0);}if(p<0 || p>palist->length){printf("Not exist!\n");return(0);}if(isNullList_seq(palist)){palist->data[0]=x;palist->length=1;return(1);}for(q=palist->length-1;q>=p;q--)palist->data[q+1]=palist->data[q] ;palist->data[p]=x;palist->length= palist->length+1;return(1);}void main(){int i;PSeqList list;list=creaeNullList_seq(); /* 创建空线性表*/printf("插入前的顺序表为:\n ");for(i=0;i<=9;i++){insertPre_seq(list,i,i*i);printf(" %d " , list->data[i]);}insertPre_seq(list,5,55);/*在下标为5的元素之前插入元素55*/ printf("\n\n插入后的顺序表为:\n ");for(i=0;i<list->length;i++)printf(" %d " , list->data[i]);printf("\n\n");}(2)运行结果2、约瑟夫问题顺序表的实现int josephus_ SeqList (PSeqList josephus_seq, int s, int m){ /*求解约瑟夫问题的出列元素序列入口参数:已经存放数据的顺序表,起始位置s,数m , 出口参数:1表示成功,0表示表中没有元素*/int s1,i,w;if ( ! josephus_seq->length){ printf(“表中无元素”);return (0); }s1=s - 1; /*data数组中下标从0开始*/printf(“输出约瑟夫序列:”);for( i= josephus_seq->length; i>0; i --;){ s1=(s1+m-1)% i; /*找到出列元素的下标*/w= josephus_seq->data[s1];printf(“%d\t”, w)Delete_SeqList(josephus_seq,s1); /*删除出列元素*/} /*for */return(1); /*成功返回*/}3、约瑟夫问题链表的实现int josephus_ LinkList (LinkList josephus_Link, int s, int m){ /*求约瑟夫问题的出列元素序列,入口参数:已经存放数据的链表头指针的地址,起始位置s,数m ,出口参数:1表示成功,0表示表中没有元素*/LinkList p,pre; /*p指向当前结点,pre指向其前驱结点*/int count;if ( ! josephus_Link){ printf(“表中无元素”);return (0);}/*找第s个元素*/p= josephus_Link;for(count=1;count<s;count++) /*查找第s个结点,用p作为第s个结点的指针*/p=p->next;printf(“输出约瑟夫序列:”);while ( p!=p->next) /*输出n-1个元素个结点*/{ for(count=1;count<m;count++){ pre=p;p=p->next;} /*for*/printf(“%d\t”, p->data);pre->next=p->next;free(p);p=pre->next;}/*while*/printf(“%d\t”,p->data); /*输出最后一个元素个结点*/free(p);return 1;}2.约瑟夫问题的求解(1)程序代码#include"stdio.h"#include"stdlib.h"typedef struct node{int data;struct node *next;}lnode,*linklist;linklist Init_josephus(void){linklist head;head=(linklist)malloc(sizeof(lnode));if(!head){printf("Apply place is faliure!\n\n");return NULL;}head->next=NULL;return head;}void Create_josephus(linklist JOS){linklist p,q,H;int x;p=H=JOS;printf("please enter elements:\n");scanf("%d",&x);p->data=x;scanf("%d",&x);while(x!=-1){q=(linklist)malloc(sizeof(lnode));q->data=x;q->next=p->next;p->next=q;p=q;scanf("%d",&x);}p->next=H;p=H;printf("\n");while(p->next!=H){printf("%3d",p->data);p=p->next;}printf("%3d",p->data);printf("\n");}int josephus_linklist(linklist josephus_link,int s,int m){linklist p,pre;int count;if(!josephus_link){printf("There are not have elements in the list!\n");return 0;}p=josephus_link;for(count=1;count<s;count++)p=p->next;printf("The list of josephus is :\n");while(p!=p->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);printf("\n");return 1;}main(){int i,j;lnode *Josephus;Josephus=Init_josephus();Create_josephus(Josephus);printf("please enter the first elements number and steps:\n");scanf("%d%d",&i,&j);josephus_linklist(Josephus,i,j);return 2;}(2)程序运行结果(3)实验总结这次实验用的是链式存储结构来解决约瑟夫问题,第一遍编写的程序中,指针使用的有点混乱,头结点不知该如何处理,指针问题解决后,在运行的过程中,头结点的问题一直解决不了即创建的表有问题还没有解决,表的创建中数据的输出里一个很大的数,原因就是头结点中的值域为空;后来将两个结构体类型的指针同时指向头指针,才将问题顺利解决。
实验一约瑟夫问题实验报告
![实验一约瑟夫问题实验报告](https://img.taocdn.com/s3/m/0f22935750e2524de5187ecc.png)
北京邮电大学电信工程学院数据结构实验报告实验名称:实验一——约瑟夫问题学生姓名: ***班级: 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++也因长时间没有碰过而稍显手生,在码程序的过程中遇到了很多问题,但通过翻看教材已基本解决。
约瑟夫环数据结构实验报告
![约瑟夫环数据结构实验报告](https://img.taocdn.com/s3/m/478552aab9f67c1cfad6195f312b3169a451eae0.png)
约瑟夫环数据结构实验报告约瑟夫环数据结构实验报告引言约瑟夫环是一种经典的数学问题,它涉及到一个有趣的数据结构。
本次实验旨在通过实现约瑟夫环数据结构,深入理解该问题,并探索其在实际应用中的潜力。
本报告将介绍实验的设计和实现过程,并分析实验结果。
实验设计在本次实验中,我们选择使用链表来实现约瑟夫环数据结构。
链表是一种非常灵活的数据结构,适合用于解决约瑟夫环问题。
我们设计了一个Josephus类,其中包含了创建环、添加元素、删除元素等操作。
实验实现1. 创建环在Josephus类中,我们首先需要创建一个循环链表。
我们使用一个头节点来表示环的起始位置。
在创建环的过程中,我们可以选择指定环的长度和起始位置。
2. 添加元素在创建环之后,我们可以通过添加元素来向约瑟夫环中插入数据。
我们可以选择在环的任意位置插入元素,并且可以动态地调整环的长度。
3. 删除元素根据约瑟夫环的规则,每次删除一个元素后,下一个元素将成为新的起始位置。
我们可以通过删除元素的操作来模拟约瑟夫环的运行过程。
在删除元素时,我们需要考虑环的长度和当前位置。
实验结果通过实验,我们得出了以下结论:1. 约瑟夫环数据结构可以有效地模拟约瑟夫环问题。
通过创建环、添加元素和删除元素的操作,我们可以模拟出约瑟夫环的运行过程,并得到最后剩下的元素。
2. 约瑟夫环数据结构具有一定的应用潜力。
除了解决约瑟夫环问题,该数据结构还可以用于其他类似的问题,如任务调度、进程管理等。
3. 约瑟夫环数据结构的时间复杂度较低。
由于约瑟夫环的特殊性质,我们可以通过简单的链表操作来实现该数据结构,使得其时间复杂度较低。
结论本次实验通过实现约瑟夫环数据结构,深入理解了该问题,并探索了其在实际应用中的潜力。
通过创建环、添加元素和删除元素的操作,我们可以模拟出约瑟夫环的运行过程,并得到最后剩下的元素。
约瑟夫环数据结构具有一定的应用潜力,并且具有较低的时间复杂度。
通过本次实验,我们对数据结构的设计和实现有了更深入的理解,并为将来的研究和应用奠定了基础。
数据结构实验教案
![数据结构实验教案](https://img.taocdn.com/s3/m/5c6a85630912a21614792969.png)
数据结构课程实验教案
合二为一。
数据结构课程实验教案
数据结构课程实验教案
合二为一。
数据结构课程实验教案
合二为一。
数据结构课程实验教案
合二为一。
数据结构课程实验教案
合二为一。
数据结构课程实验教案
合二为一。
数据结构课程实验教案
填表说明:1、每项页面大小可自行添减;2、教学内容与讨论、思考题、作业部分可合二为一。
数据结构课程实验教案
如对您有帮助,欢迎下载支持,谢谢!
填表说明:1、每项页面大小可自行添减;2、教学内容与讨论、思考题、作业部分可合二为一。
11。
数据结构与算法(Python版):用队列(Queue)处理约瑟夫问题
![数据结构与算法(Python版):用队列(Queue)处理约瑟夫问题](https://img.taocdn.com/s3/m/45e827e5162ded630b1c59eef8c75fbfc77d946f.png)
数据结构与算法(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!这个⼈不会被杀死。
数据结构约瑟夫环问题
![数据结构约瑟夫环问题](https://img.taocdn.com/s3/m/feab3bbff18583d04864594e.png)
数据结构实验报告题目:约瑟夫环问题一.设计内容[问题描述]约瑟夫环问题的一种描述是:编号为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。
数据结构实验报告约瑟夫环
![数据结构实验报告约瑟夫环](https://img.taocdn.com/s3/m/f04b1aa59a89680203d8ce2f0066f5335b816745.png)
数据结构实验报告约瑟夫环约瑟夫环是一个古老而有趣的问题,也是数据结构中一个经典的应用。
它的故事发生在公元前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为报数的次数。
除了链表,还有其他数据结构可以用来解决约瑟夫环问题。
数据结构——约瑟夫顺序存储
![数据结构——约瑟夫顺序存储](https://img.taocdn.com/s3/m/7a328b4f336c1eb91a375d4b.png)
#include<stdio.h>#define Msize 100typedef int ElemType;typedef struct{ElemType data[Msize];int length;}sqlist;void IniList(sqlist &L) //初始化线性表{L.length=0;}void CreatList(sqlist &L,int n) //创建线性表{int i;for(i=0;i<n;i++)L.data[i]=i+1;L.length=n;}int IsEmpty(sqlist L) //判断线性表空{if(L.length==0) return 1;else return 0;}int Get(sqlist L,int i) //取线性表元素{if((i>=0)&&(i<L.length))return L.data[i];}int Delete(sqlist &L,int i){int k;if(i<0||i>=L.length)return 0;else{for(k=i;k<L.length;k++)L.data[k]=L.data[k+1];L.length--;}return 1;}void main(){int n,m,a;printf("请输入约瑟夫元素个数n=");scanf("%d",&n);printf("请输入约瑟夫问题间隔m=");scanf("%d",&m);printf("请输入起始位置为:");scanf("%d",&a);a=a-1; //先将a减1,以与下面的算法相对应;sqlist La;IniList(La);CreatList(La,n);printf("约瑟夫出环顺序为:");while(La.length!=0){a=(a+m-1)%La.length;printf("%4d",Get(La,a));Delete(La,a);}printf("\n");}。
数据结构实验报告约瑟夫环
![数据结构实验报告约瑟夫环](https://img.taocdn.com/s3/m/bf5113b7f71fb7360b4c2e3f5727a5e9856a2789.png)
数据结构实验报告约瑟夫环约瑟夫环是一个经典的问题,涉及到数据结构中的循环链表。
在本次数据结构实验中,我们将学习如何使用循环链表来解决约瑟夫环问题。
约瑟夫环问题最早出现在古代,传说中的犹太历史学家约瑟夫斯·弗拉维奥(Josephus Flavius)在围攻耶路撒冷时,为了避免被罗马人俘虏,与其他39名犹太人躲进一个洞穴中。
他们决定宁愿自杀,也不愿被敌人俘虏。
于是,他们排成一个圆圈,从第一个人开始,每次数到第七个人,就将他杀死。
最后剩下的人将获得自由。
在这个问题中,我们需要实现一个循环链表,其中每个节点表示一个人。
我们可以使用一个整数来表示每个人的编号。
首先,我们需要创建一个循环链表,并将所有人的编号依次添加到链表中。
接下来,我们需要使用一个循环来模拟每次数到第七个人的过程。
我们可以使用一个指针来指向当前节点,然后将指针移动到下一个节点,直到数到第七个人为止。
一旦数到第七个人,我们就将该节点从链表中删除,并记录下该节点的编号。
然后,我们继续从下一个节点开始数数,直到只剩下一个节点为止。
在实现这个算法时,我们可以使用一个循环链表的数据结构来表示约瑟夫环。
循环链表是一种特殊的链表,其中最后一个节点的指针指向第一个节点。
这样,我们就可以实现循环遍历链表的功能。
在实验中,我们可以使用C语言来实现循环链表和约瑟夫环算法。
首先,我们需要定义一个节点结构体,其中包含一个整数字段用于存储编号,以及一个指针字段用于指向下一个节点。
然后,我们可以实现创建链表、添加节点、删除节点等基本操作。
接下来,我们可以编写一个函数来实现约瑟夫环算法。
该函数接受两个参数,分别是参与游戏的人数和每次数到第几个人。
在函数内部,我们可以创建一个循环链表,并将所有人的编号添加到链表中。
然后,我们可以使用一个循环来模拟每次数到第几个人的过程,直到只剩下一个节点为止。
在每次数到第几个人时,我们可以删除该节点,并记录下其编号。
最后,我们可以返回最后剩下的节点的编号。
约瑟夫环问题实验报告
![约瑟夫环问题实验报告](https://img.taocdn.com/s3/m/66d4c77a67ec102de2bd89dd.png)
//报数为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为一个根据实际问题定义的一个足够大的整数。
约瑟夫问题实验报告
![约瑟夫问题实验报告](https://img.taocdn.com/s3/m/76ef37eb5727a5e9846a6172.png)
约瑟夫问题实验报告(文章一):约瑟夫问题数据结构实验报告中南民族大学管理学院学生实验报告实验项目: 约瑟夫问题课程名称:数据结构年级:专业:信息管理与信息系统指导教师:实验地点:管理学院综合实验室完成日期:小组成员:学年度第(一)、实验目的(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 报数,如此下去,直至所有人全部出列为止。
试设计一个程序求出出列顺序。
基本要求是利用单向循环链表存储结构模拟此过程,按照出列的顺序印出各人的编号。
Josephu约瑟夫问题java实现(环形链表)
![Josephu约瑟夫问题java实现(环形链表)](https://img.taocdn.com/s3/m/79b93986690203d8ce2f0066f5335a8102d2663c.png)
Josephu约瑟夫问题java实现(环形链表)5.4.1约瑟夫问题Josephu(约瑟夫、约瑟夫环) 问题为:设编号为 1,2,… n 的 n 个⼈围坐⼀圈,约定编号为 k(1<=k<=n)的⼈从 1 开始报数,数 到 m 的那个⼈出列,它的下⼀位⼜从 1 开始报数,数到 m 的那个⼈⼜出列,依次类推,直到所有⼈出列为⽌,由 此产⽣⼀个出队编号的序列。
5.4.2解决思路⽤⼀个不带头结点的循环链表来处理 Josephu 问题:先构成⼀个有 n 个结点的单循环链表,然后由 k 结点起从 1 开 始计数,计到 m 时,对应结点从链表中删除,然后再从被删除结点的下⼀个结点⼜从 1 开始计数,直到最后⼀个 尚硅⾕ Java 数据结构和算法 更多 Java –⼤数据 –前端 –python ⼈⼯智能 -区块链资料下载,可访问百度:尚硅⾕官⽹ 第 55页 结点从链表中删除算法结束。
代码实现//约瑟夫问题-环形链表public class Josepfu {public static void main(String[] args) {CircleSingleLinkedList circleSingleLinkedList = new CircleSingleLinkedList();circleSingleLinkedList.addBoy(5);// 加⼊ 5 个⼩孩节点circleSingleLinkedList.showBoy();circleSingleLinkedList.countBoy(1, 2, 5);}}//环形链表class CircleSingleLinkedList{//指向链表的第⼀个节点private Boy first = null;//添加num个⼩孩节点public void addBoy(int num){if (num<1)throw new RuntimeException("输⼊值错误");Boy curBoy = null;for(int i=1;i<=num;i++){Boy boy = new Boy(i);if (i==1){first = boy;first.setNext(first);//形成环curBoy = first;}else{boy.setNext(first);curBoy.setNext(boy);curBoy = boy;}}}// 根据⽤户的输⼊,计算出⼩孩出圈的顺序/**** @param startNo* 表⽰从第⼏个⼩孩开始数数* @param countNum* 表⽰数⼏下* @param nums* 表⽰最初有多少⼩孩在圈中*/public void countBoy(int startNo, int countNum, int nums) {if (nums<1||countNum<1||first==null||startNo<1||startNo>nums)throw new RuntimeException("参数有误,从新输⼊!!");//创建辅助指针,指向环形链表的最后⼀个节点Boy helper = first;while (helper.getNext()!=first){helper = helper.getNext();}//移动helper和first,使从第startNo个⼩孩开始数for (int i=0;i<(startNo-1);i++){helper = helper.getNext();first = first.getNext();}//开始数数,出圈while (helper!=first){//报数for (int i=0;i<(countNum-1);i++){helper = helper.getNext();first = first.getNext();}System.out.println("⼩孩"+ first.getNo() +"出队列:" );first = first.getNext();helper.setNext(first);}System.out.println("最后的⼩孩:"+ first.getNo());}//遍历环形链表public void showBoy(){if (first==null)throw new RuntimeException("链表为空");System.out.println("⼩孩的编号: "+first.getNo());//first⽆法移动,创建中介节点遍历链表Boy curBoy = first.getNext();//当中介节点再⼀次回到first时,表⽰链表遍历完成while (curBoy!=first){System.out.println("⼩孩的编号: "+curBoy.getNo()); curBoy = curBoy.getNext();}}}//创建boy类表⽰⼀个节点class Boy{private int no;private Boy next;public Boy(int no) {this.no = no;}public int getNo() {return no;}public void setNo(int no) {this.no = no;}public Boy getNext() {return next;}public void setNext(Boy next) {this.next = next;}}。
约瑟夫环问题 实验报告完整版
![约瑟夫环问题 实验报告完整版](https://img.taocdn.com/s3/m/5b0c897c7e21af45b307a865.png)
(2)约瑟夫环报数的算法在运行为循环方式,报数者除非本身已经出去,否则继续顺序报数,其报数循环的代码为
void Joseph(NODE *p,int number,int n)
{
int;=number; i++)
{
for(j=1; j<n-1; j++)
(2)基本要求
建立模型,确定存储结构。
对任意n个人,密码为m,实现约瑟夫环问题。
出圈的顺序可以依次输出,也可以用一个数组存储。
(3)思考:
采用顺序存储结构如何实现约瑟夫环问题?
如果每个人持有的密码不同,应如何实现约瑟夫环问题?
2.数据结构设计
由于约瑟夫环问题本身具有循环性质,考虑采用循环链表,为了统一对表中任意结点的操作,循环链表不带头结点。将循环链表的结点定义为如下结构类型:
5.运行测试与分析
(1)输出提示,如图1.2所示。
(2)根据提示,输入圈内人数n和每个人持有的密码m如图1.3所示。
(3)输出结果如图1.4所示
分析
6.实验收获及思考
通过该实验,我进一步增强了对于链表的理解,也对链表的操作和实现更为熟悉,熟练掌握了如何实现置空表、求表的长度、取结点、定位运算、插入运算、删除运算、建立不带头结点的单链表(头插入法建表)、建立带头结点的单链表(尾插入法建表),输出带头结点的单链表等操作。同时,锻炼了实际操作时的动手能力。
{
p=p->next;
}
q=p->next;
p->next=q->next;
p=p->next;
printf("第%3d个出圈的人是:%3d\n",i,q->value);
约瑟夫环上机实验报告
![约瑟夫环上机实验报告](https://img.taocdn.com/s3/m/a3f063523868011ca300a6c30c2259010302f370.png)
一、实验目的1. 理解并掌握约瑟夫环问题的基本概念及其在数据结构中的应用。
2. 掌握单向循环链表的基本操作,包括创建、插入、删除和遍历等。
3. 通过编程实践,提高问题解决能力和编程技巧。
4. 深入理解循环链表在解决实际问题中的应用,并学会使用循环链表解决类似问题。
二、实验内容本次实验主要涉及以下内容:1. 创建单向循环链表,实现约瑟夫环问题的基本功能。
2. 实现约瑟夫环问题的出列顺序输出。
3. 优化算法,提高程序执行效率。
三、实验步骤1. 创建单向循环链表- 定义链表节点结构体,包含编号和指向下一个节点的指针。
- 实现创建单向循环链表的函数,输入人数m,创建一个包含m个节点的循环链表。
2. 实现约瑟夫环问题- 定义一个函数,输入起始编号s、报数上限n,实现约瑟夫环问题的出列顺序输出。
- 在函数中,使用循环链表遍历,找到起始节点,然后按照报数上限n进行出列操作。
3. 输出出列顺序- 将出列顺序存储在一个数组中,遍历循环链表,将出列节点的编号依次存入数组。
4. 优化算法- 通过减少循环次数和避免重复计算,提高程序执行效率。
四、实验代码```c#include <stdio.h>#include <stdlib.h>// 定义链表节点结构体typedef struct Node {int number;struct Node next;} Node;// 创建单向循环链表Node createList(int m) {Node head = (Node)malloc(sizeof(Node));head->number = 1;Node tail = head;for (int i = 2; i <= m; i++) {Node newNode = (Node)malloc(sizeof(Node));newNode->number = i;tail->next = newNode;tail = newNode;}tail->next = head; // 形成循环链表return head;}// 实现约瑟夫环问题void josephus(int m, int s, int n) {Node head = createList(m);Node current = head;// 找到起始节点for (int i = 1; i < s; i++) {current = current->next;}// 输出出列顺序while (current->next != current) { for (int i = 1; i < n; i++) { current = current->next;}printf("%d ", current->number); Node temp = current;current = current->next;free(temp);}printf("%d\n", current->number);free(current);}int main() {int m, s, n;printf("请输入人数m: ");scanf("%d", &m);printf("请输入起始编号s: ");scanf("%d", &s);printf("请输入报数上限n: ");scanf("%d", &n);josephus(m, s, n);return 0;}```五、实验结果与分析1. 结果分析通过实验,成功实现了约瑟夫环问题的出列顺序输出。
数据结构实验报告一-约瑟夫环问题
![数据结构实验报告一-约瑟夫环问题](https://img.taocdn.com/s3/m/7697bd1dff00bed5b9f31d56.png)
实验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)算法的基本思想:约瑟夫环问题中的数据是人所在的位置,而这种数据是存在“第一元素、最后元素”,并且存在“唯一的前驱和后继的”,符合线性表的特点。
约瑟夫环问题的两种解法(循环链表和公式法)
![约瑟夫环问题的两种解法(循环链表和公式法)](https://img.taocdn.com/s3/m/d7ff861b78563c1ec5da50e2524de518964bd304.png)
约瑟夫环问题的两种解法(循环链表和公式法)问题描述这⾥是数据结构课堂上的描述: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),显然,这不是⼀个好的算法。
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
HUNAN UNIVERSITY课程实习报告题目:约瑟夫问题学生姓名刘海龙学生学号************专业班级软件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、运行程序时提示输入人数和报数值,注意人数和报数值均应为正整数,输入不合法时要求重新输入,在输入时,两者用逗号隔开。