工作报告之约瑟夫环实验报告总结

约瑟夫环实验报告总结

【篇一:约瑟夫环实验报告】

实验报告

课程名称:数据结构

实验名称:顺序表和链表的应用

实验编号:实验一

指导教师:

一、实验目的

(1)掌握线性表的基本操作(插入、删除、查找)以及线性表合

并等运算在顺序存储结

构、链式存储结构上的实现。重点掌握链式存储结构实现的各种操作。

(2)掌握线性表的链式存储结构的应用。

二、实验内容与实验步骤

(1)实验内容:

实现约瑟夫环,约瑟夫环(joseph)问题的一种描述是:编号为1、2、3……n的n个人按照顺时针方向围坐一圈,每人持有一个密码(正整数)。一开始任选一个正整数作为报数的上限值m,从第一

个人开始按照顺时针的方向自1开始顺序报数,报到m时停止报数。报m的人出列,将他的密码作为新的m值,从他的顺时针方向上的

下一个人开始重新从1报数,如此下去,直至所有人全部出列为止。设计一个程序求出出列顺序。

(2)抽象数据类型和设计的函数描述,说明解决设想。

首先定义一个链表,用其中的data项存储每个人的编号,用password项存储每个人所持有的密码,并且声明一个指针。之后使用creatlist_cl函数来创建一个循环链表,在其中的data和password中存入编号和密码,最后使最后一个节点的next指向l,使其能够形成循环队列。定义了函数display来显示链表当中的内容,以确定存储的数据没有错误。定义了函数delete_l来实现约瑟夫环

中依次删除的功能,依次比较,如果某个人所持的密码和m值相等,则删除这个结点,并且输出此时该结点的编号和密码,实现出列的

功能。

(3)简短明确地写出实验所采用的存储结构,并加以说明。

该实验我主要采用的是线性表的链式存储结构,首先定义了链表的结构,其中包括data项和password项,分别存储每个人的编号和所持密码,还声明了指向下一个结点的指针,该指针可以连接各个结点,并且将最后一个结点的指针指向第一个结点使之成为一个循环链表。

三、实验环境

操作系统:windows 7

调试软件名称:vc++

版本号:6.0

上机地点:综合楼311

四、实验过程与分析

(1)主要的函数或操作内部的主要算法,分析这个算法的时、空复杂度,并说明设计的巧班级:学号:姓名:组号:实验成绩:批阅教师签字:实验日期:实验时间:

妙之处。

本实验中主要的函数包括创建链表、显示链表内容和出列过程四个部分。主要函数的代

码如下:

创建链表:

typedef int datatype;

typedef struct node//链表的定义

{

datatype data;

int password;

struct node *next;

}listnode,*clinklist;

void creatlist_cl(clinklist *l,int n)//创建一个链表

{

int i,pin;

clinklist p,q;

(*l)=(clinklist)malloc(sizeof(listnode));

if((*l)==null)

printf(error\n);

else

(*l)-next=null;

q=*l;

for(i=0;in;i++)

{

p=(clinklist)malloc(sizeof(listnode));

if(p==null)

printf(error\n);

printf(请输入第%d个人的密码:,i+1);

scanf(%d,pin);

p-data=i+1;

p-password=pin;

q-next=null;

q-next=p;

q=p;

}

q-next=(*l)-next;//指向l结点,形成

}

创建这个链表的时间复杂度为o(n),空间复杂度为o(n2)。显示链表中的信息内容:

void display(clinklist *l,int n)

{

int i;

clinklist p;

p=(*l)-next;

printf(\n显示链表内容\n);

for(i=0;in;i++)

{

printf(编号:%2d密码:%d\n,p-data,p-password);

p=p-next;

}

}

该算法的时间复杂度为o(n),空间复杂度为o(n2)。

删除结点,完成出列功能:

void delete_l(clinklist *l,int n,int m)

{

int i=0,j;

clinklist p,q;

q=(*l);

p=(*l)-next;

printf(\n删除的顺序:\n);

while(in)

{

for(j=0;jm-1;j++)

{

q=p;

p=p-next;

}

printf(编号:%d 密码:%d\n,p-data,p-password);

m=p-password;

q-next=p-next;

free(p);

p=q-next;

n--;

}

}

该算法的时间复杂度为o(n2),空间复杂度为o(n2)。

该设计的巧妙之处在于并不需要额外的空间来存储数据,因而空间复杂度较低,而且线

性表的链式存储结构可以用物理位置上的邻接关系来表示结点间的逻辑关系,这样使读者在

阅读代码的过程中可以更加方便和便于理解。它可以随机存取表中的任一结点,还可以免插

入和删除操作带来的大量的结点的移动,能给结点动态分配内存,这样就不存在存储空间不

足的情况,而且循环链表还可以方便的从链表的最后一个结点遍历到链表的第一个结点。使

操作更加方便。

(2)你在调试过程中发现了怎样的问题?又做了怎样的改进

1)在最开始的调试阶段,我发现链表插入结束之后,不能按照正常情况下输出链表的

内容,只能正常显示第一个人的数据,在显示第二个人的信息是数据为乱码。之后我发现,

在插入链表的过程中,我是在执行循环插入数据的循环中将结点的指针指向了第一个结点,

因而,在进行链表显示的过程中,第二个结点的内容不是正常的数据。之后我将

q-next=(*l)-next;这条指令放到了整个插入循环的外部,这样表示在插入所有数据之后,

最后一个结点的指针指向了第一个结点,形成了一个循环队列,此时链表的数据显示正确。

2)再次调试时,我发现人员出列时,只有第一个人出列正常,在第二个人出列时程序

自动终止,不能正常显示之后出列的人的信息,并且程序自动终止运行,经过检查我发现在

经过一次删除后,没有将指针指向下一个结点,因而出现问题。经过更改,程序运行正常。

3)在实验的开始阶段,数据遍历总是出现问题,经过查找资料我发现了约瑟夫环头结

点的特殊性,因此我不再使用头结点,程序便恢复正常了。

(2)测试结果

五、实验结果总结

回答以下问题:

(1)你的测试充分吗?为什么?你是怎样考虑的?

答:我认为我的测试充分,因为我随机选用了很多组不同的数据进行测试,并且

每次测试的结果都是正确的答案,这样选取的数据具有很强的随机性,具有代表性,

因而我认为我的测试比较充分。

(2)你的存储结构的选取是不是很适合这个应用?为什么?

答:我认为我选取的线性链式存储结构适合这个应用,因为首先此题中描述的情

景中表示人们按照顺时针的方向进行排队,此时头尾相连,这与循环链表的结构十分

相似,使用循环链表的结构,这样可以很方便的从链表的最后一个结点访问到链表的

第一个结点,并且这样的存储方式是用物理位置上的邻接关系来表示结点间的逻辑关

系,根据这个特点,该种结构可以随机存取表中的任一结点,而且它也可以避免插入

和删除操作带来的大量结点的移动,并且可以随时分配空间和释放空间,这样可以减

少空间的使用量,并且可以做到灵活的扩充空间,这样的结构很适合这个应用。

(3)用一段简短的代码及说明论述你的应用中有关插入和删除元

素是如何做的?

答:插入元素:首先定义了两个临时指针p和q来分别表示新插入

结点的指针和

第一个结点的指针,在每次插入之前应该动态的分配内存,输入要

输入的信息,并且

将各种数据存储到链表中相应的项里,将前一个结点的next赋值为空,再将前一个结

点的指针指向下一个结点,此时完成一个元素的插入。依次类推,

运用循环来实现所

有人的数据的插入,关键代码如下:

p=(clinklist)malloc(sizeof(listnode));

if(p==null)

printf(error\n);

printf(请输入第%d个人的密码:,i+1);

scanf(%d,pin);

p-data=i+1;

p-password=pin;

q-next=null;

q-next=p;

q=p;

删除元素:进行循环来实现每个元素出列的功能,首先每个人进行

循环,一

次进行报数,在报到m-1之前都不进行删除元素这个动作,在m时,把此时结点中的

password中的数值赋给m然后运用q-next=p-next;将结点删除,

同时释放结点p,

将人数减1,以此类推完成所有的删除操作,直到所有的元素出列,关键代码如下:

while(in)

{

for(j=0;jm-1;j++)

{

q=p;

p=p-next;

}

printf(编号:%d 密码:%d\n,p-data,p-password);

m=p-password;

q-next=p-next;

free(p);

p=q-next;

n--;

}

(4)在你的应用中是否用到了头结点?你觉得使用头结点为你带

来方便了吗?

答:在我的应用中我没有用到头结点。在实验的一开始,我使用了

头结点,但是

使用头结点给数据的遍历带来了困难,因此我便放弃使用头结点。(5)源程序的大致的执行过程是怎样的?

答:首先用编译器编写一个.c的文件,然后编译生成.obj的文件,

通过连接将目标

文件连接生成一个.exe文件,之后运行文件就可以执行了。

六、附录

(1)实验设想和建议

这次实验提高了我对数据结构中关于循环链表和顺序表的理解,提

高了我的编程

能力,学校以后最好可以增加实验课的课时,这样我们可以更大程

度的提高自己的编

程能力。另外我认为该实验不仅可以使用使用链表指针来实现,还

可以使用数组来模

拟链表来实现约瑟夫环,用数组的下标来指向前一个和后一个元素,之后进行删除来

实现约瑟夫环。

(2)参考资料:《数据结构(第二版)》闫玉宝编著清华大学出

版社

【篇二:约瑟夫环实验报告】

《数据结构与算法设计》

实验报告

——实验一

学院:自动化学院

班级:06111001

学号:1120101525

姓名:王冬

一、实验目的

1、熟悉vc环境,学习使用c语言利用链表的存储结构解决实际的

问题。

2、在编程、上机调试的过程中,加深对线性链表这种数据结构的

基本概念理解。

3、锻炼较强的思维和动手能力和更加了解编程思想和编程技巧。

二、实验内容

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、概要设计

为了解决约瑟夫环的问题,我们可以建立单向环表来存储每个人的

信息(该人的编号以及其下一个人的编号),及结点,人后通过查

找每个结点,完成相应的操作来解决约瑟夫问题。

(1)抽象数据类型定义

adt joh{

数据对象:d={ai|ai?elemset,i?1,2,?,n,n?0} 数据关系:

r1={?ai?1,ai?|ai?1,ai?d,i?1,2,?,n} 基本操作: create(j, n) 操作结果:构造一个有n个结点的单向环表j。 show(j)初始条件:单向环

表j已存在。操作结果:按顺序在屏幕上输出j的数据元素。calculate( j,s,n)

初始条件:单向环表j已存在,s0,n0,s环表

结点数。

操作结果:返回约瑟夫环的计算结果。

}adt joh

(2)宏定义

#define null 0

#define ok 1

#define error -1

(3)主程序流程

(4)

程序分为下述模块:

1)主函数模块——执行输入调用其他的功能函数

2)创建环表模块——创建单向环表

3)计算处理模块——计算出要出列的标号并输出

4)显示模块——输出建立好的环表

调用关系如下:

2、详细设计

(1)数据类型设计

typedef int elemtype; //元素类型 typedef struct {

elemtype data;

struct joh *next;

}joh, *linklist,*p;//结点类型,指针类型

(2)操作算法

status create(linklist j,int n){

//创建一个有n个结点的单向环表 if(n=0)

return error; //n0错误

j=(linklist)malloc(sizeof(j));

j-data=1;

j-next=j;//建立第一个结点

for(int i=n;i1;--i){

p=(linklist)malloc(sizeof(j));

p-data=i;

【篇三:约瑟夫环问题_实验报告】

年级12级学号 12061633姓名徐超杰

一、实验目的

本实验的目的是进一步理解线性表的逻辑结构和存储结构,进一步提高使用理

论知识指导解决实际问题的能力。

二、实验问题描述

人从1

开始报数,数到m的那个人出列,他的下一位又从1开始报数,数到m的那个人

又出列,依次类推,直到所有人出列为止,由此产生一个出队编号的序列。

三、实验步骤

1、实验问题分析

①由于当某个人退出圆圈后,报数的工作要从下一个人开始继续,剩下的人

仍要是围成一个圆圈,可以使用循环表;由于退出圆圈的工作对应着表中结点的

删除操作,对于这种删除操作频繁的情况,应该选用效率较高的链表结构;为了

程序指针每一次都指向一个具体的代表一个人的结点而不需要进行判断,链表不

带表头结点。所以,对于所有人围成的圆圈所对对应的数据结构采用一个不带头

结点的循环链表来描述。设头指针为p,并根据具体情况移动

可以采用数据类型定义:

typedef struct node

{

int number;

struct node *next;

}lnode,*linklist;

②为了记录退出的人的先后顺序,采用一个顺序表进行存储,程序结束后再

输入依次退出的人的编号顺序。由于只记录各个结点的number值就可以,所以

定义一个整型一维数组。如“int quite[n];”n为一个根据实际问题定义的一

个足够大的整数。

2、功能(函数)设计

根据上述分析,该算法可以由3个功能函数实现。main()用做数据的输入和

函数的调用,init()做链表的初始化工作,使用josephus()做删除结点和保存

输出顺序的工作,outring()完成序列的输出工作。

1.建立单循环链表函数 linklist initringlist(int n);

2.产生josephus顺序函数 void josephus(linklist l,int n,int k,int m,int quit[n])

3.输出顺序表void print(int n,int quit[n])

四、实验结果(程序)及分析

1.实验的的源代码:

// 约瑟夫环问题.cpp : defines the entry point for the console application.

//

//#include stdafx.h

#includeiostream.h

#define n 34

typedef struct node

{

int data;

struct node *next;

}lnode,*linklist;

linklist initringlist(int n)//尾插法建立单循环链表

{

lnode *l,*r,*s;

l=new lnode; //不带头结点

r=l;

for(int i=1;in;i++)

{

s=new lnode;

r-data=i;

r-next=s;

r=s;

}

r-data=n;

r-next=l; //链表首尾相连

l=r; //l指向循环链表的尾结点

return l;

}

void josephus(linklist l,int n,int k,int m,int quit[n])

{

int i,j;

lnode *p,*q;

p=l;

for(int r=1;rm;r++)

p=p-next;

for(i=0;in;i++)

{

for(j=1;j=k-1;j++)

p=p-next;

q=p-next;

p-next=q-next;

quit[i]=q-data;

delete q;

}

}

void print(int n,int quit[n])

{

int i;

for(i=0;in;i++)

coutquit[i] ;

coutendl;

}

int main(int argc, char* argv[])

{

linklist l;

int n;

int k;

int m;

cout请输入围坐一圈的人数n的值:endl;

cinn;

l=initringlist(n);

int quit[n];

cout约定从编号为m的值开始数,请输入m的值:endl; cinm;

while(mn||m1)

{

cout输入错误,请重新输入:endl;

cinm;

}

cout要求数到k的人出列,请输入k的值:endl;

cink;

cout顺序为:;

josephus(l,n,k,m,quit);

print(n,quit);

return 0;

}

2.测试数据

a.当n的初始值为7,k的值为5,m的值为1时,正确的出列顺序为:5、3、2、4、7、1、6,经程序运行测试,结果如下:

可知程序运行正确。

b.程序的容错性测试,当输入m的值不符合问题约定时,应有错误提示给用户,指导用户正确输入,并做出相应处理,保证程序运行。测试如下:

3.测试中出现的问题

a.在此次编写中一个for循环出现的位置发生了错误,但程序仍可运行,可是这样运行出来的数据的顺序会发生错误,解决此类问题的方法是多运行几次,而且应该有这样一个概念运行出来和调试没有错误不一定代表没有了错误。

相关文档
最新文档