约瑟夫问题实验报告

合集下载
  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

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和m
int i=1;
Node *p=head;
Node *pre=NULL;
执行:”pre=p;p=p->next;”m
次,模拟报数并移动指针
此时,p->data便是出列编
号,输出其值
删除p指向的节点,令p
之后的节点为head
返回head
图3——子函数:当链表中的结点个数大于1,调用
此函数,实现目标出列,并删除该节点,另该节点下
一结点为头结点,返回修改后的头结点
二、详细设计
本程序核心算法主要有两部分:一是建立循环链表,二是实现目标人员的出列。

建立循环链表:
1.判断原链表是否为空,是的话,执行第2步,否则,执行第3步;
2.为头结点head分配存储空间,并令其指针域指向本身,head->data=1,用pr指向head,返回第1步;
3.判断计数器i(初始为2)是否不大于n,是的话,执行第4步,否则,执行第5步;
4.为节点p分配存储空间,并令其指针域指向head,给p->data赋值为i,令pr->next=p,用pr指向head,i增1,返回第3步;
5.返回head到主函数。

实现目标人员的出列:
1.输入从第k个人开始报数,在主函数中实现头指针的移动,修改后,执行第2步;
2.判断链表中的节点数是否大于1,是的话,执行第3步,否则,执行第5步;
3.调用子函数Search,定义指针:Node *p=head,执行第4步;
4.根据输入中第m个人出列,移动指针p m-1次,令其指向要出列的目标节点,输出其数据域p->data,并令此节点之后的第一个节点为head,删除目标节点,返回head到主函数,执行第2步;
5.输出head->data,并释放链表的剩余空间,结束程序。

代码清单:
#include<stdio.h>
#include<stdlib.h>
typedef struct CLNode{
int data;
struct CLNode *next;
}Node;//定义单向循环链表结构体
Node* Creat(Node* head,int n);
Node* Search(Node* head,int m);
int main()
{
int m,n,k,j;
Node *head=NULL;
printf("How many person?");
scanf("%d",&n);
if(n<=0){
printf("Can't be zero!\n");
exit(0);
}
head=Creat(head,n); //建立链表
printf("Which number do you want to start:");
scanf("%d",&k);
printf("Which number should be out:");
scanf("%d",&m);
for(j=1;j<k;j++){
head=head->next;
}
printf("\n");
if(m==1){//如果是按顺序输出,不必调用Search函数
for(j=0;j<n;j++){
printf("output number is:%d\n",head->data);
head=head->next;
}
}
else{
while(head!=head->next){
head=Search(head,m);//返回修改后的头结点
}
if(head!=NULL){
printf("output number is:%d\n",head->data);//输出最后的节点}
}
printf("****END****\n");
free(head);
return 0;
}
/*Create函数:参数为Node型结点指针和数量n
-建立单循环链表,如果成功,返回该链表的头指针*/
Node* Creat(Node *head,int n) {
Node *p=NULL;
Node *pr=head;
int i;
for (i=1; i<=n;i++){
if (head==NULL)
{
head=(Node*)malloc(sizeof(Node));
if(head==NULL){
printf("Allcate memory error!\n");
exit(1);
}
head->next=head;
head->data=i;
pr=head;
}
else
{
p=(Node*)malloc(sizeof(Node));
if(p==NULL){
printf("Allcate memory error!\n");
exit(1);
}
pr->next=p;
p->data=i;
p->next=head;
pr=p;
}
}
return head;
}
/*Search函数:输出从head开始数第m个结点的数据域,令该结点之后的结点为头结点,
并返回它,数据域从它开始再次循环*/
Node* Search(Node *head,int m)
{
int i=1;
Node *cur_p=head,*pre=NULL;//head为开始数1的人
while(i<m){//数数,移动指针
pre=cur_p;
cur_p=cur_p->next;
i++;
}
printf("output number is:%d\n",cur_p->data);
pre->next=cur_p->next;
head=pre->next;//修改头指针
free(cur_p);//删除该节点
return head;
}
三、算法的时空分析
在程序中要求输入三个变量:n,总人数;k,从第几个人开始报数;m,第几个人出列。

Creat函数中,建立链表这个算法的复杂规模与n有关,其时间复杂度为:T(n)=O(n)。

main函数中,移动链表头指针到指定开始位置这个算法的复杂规模与k有关,其时间复杂度为:T(n)=O(k)。

main函数中,如果不调用Search函数,时间复杂度为:T(n)=O(n);调用Search函数的话,调用次数为n-1,在Search函数中实现一次出列这个算法的时间复杂度与m有关,故实现全部出列这个算法的时间复杂度为:T(n)=O(m*n)。

综上,该程序的时间复杂度为:T(n)=O(m*n)
四、测试结果
输入的数据理论输出结果实际输出结果n k m
1 1 1 1 1
2 1 1 1,2 1,2
2 2 1 2,1 2,1
2 2 2 1,2 1,2
1 1
2 1 1
1 2 1 1 1
5 1 4 4,3,5,2,1 4,3,5,2,1
5 4 1 4,5,1,2,3 4,5,1,2,3
5 5 5 4,5,2,3,1 4,5,2,3,1
五、使用说明和总结
输入与输出格式如图:
图4——运行过程
其中,n、k、m分别对应着第一个、第二个、第三个输入参数
这个程序真是给我带来了不少的烦恼与喜悦。

在建立循环链表这个结构的时候没怎么遇到问题,问题主要出在实现出列上。

一开始我的Search函数定义如下:
int Search(Node* head,int m)
/*Search函数:返回为第m个结点的数据域,
并令此节点之后的结点为头结点,然后删除它*/
int Search(Node *head,int m)
{
int i=1;
int data;
Node *cur_p=head,*pre=NULL;//head为开始数1的人
//printf("**now head data ** is %d\n",head->data);
if(head->next==head){
return head->data;
}
while(i<m){//数数,移动指针
pre=cur_p;
cur_p=cur_p->next;
i++;
}
pre->next=cur_p->next;
free(cur_p);
head=pre->next;//改变头指针
return data;
}
调用如下:
while(head->next!=head){
printf("output data is %d\n",Search(head,m));//输出
}
这里,返回的是目标结点的数据域。

但是在实际运行中出现了以下情况:
图5——错误结果1
经过多次调试,终于发现了出现这种情况的原因,我在函数中传递的参数Node *head实际上是主函数中Node *head的拷贝,因而在函数中改变头结点的操作“head=pre->next;//改变头指针”最后并没有成功。

从而导致错误结果。

随后我改变函数返回值为“Node*”,而在函数中输出目标数据,这样就可以将修改后的head传递回main,而正确实现下一个输出。

后来,在进一步测试中,我又发现了这样一个问题:如果我输入2,1,1,理论输出应该是:1,2.但实际输出为:
图6——错误结果2
进一步分析,是因为在Search函数中,我默认输入的m应该大于1,没有考虑到按照顺序输出的情况,
因此,经过多次修改和均衡,我在主函数中加入了判定:
图7——局部截图
虽然是不好的风格,但在这里的确是最简单的一种做法。

总结一下我的体会:要想写出漂亮的代码,要注意“磨刀不误砍柴工”,事先画出流程图,并注重边
际分析于后期测试。

我也深切体会到了自己排错能力的不足。

我要在这些方面努力改进。

相关文档
最新文档