数据结构约瑟夫环课程方案Word格式文档下载.docx
《数据结构约瑟夫环课程方案Word格式文档下载.docx》由会员分享,可在线阅读,更多相关《数据结构约瑟夫环课程方案Word格式文档下载.docx(10页珍藏版)》请在冰豆网上搜索。
2.1类LinkList3
2.2类Joseph3
2.3类异常处理3
3详细设计和实现3
3.1创建结点Node3
3.2创建双向循环链表4
3.3从链表中删除结点5
4调试与操作说明5
4.1调试情况5
4.2操作说明6
总结8
致谢9
参考文献10
1需求分析
1.1功能分析
本次选做的课程设计是改进约瑟夫(Joseph>
环问题。
我选择了和薛晶两个人来完成本次课程设计的作业。
约瑟夫环问题是一个古老的数学问题,本次课题要求用程序语言的方式解决数学问题。
此问题仅使用单循环链表就可以解决此问题。
而改进的约瑟夫问题通过运用双向循环链表,同样也能方便地解决。
在建立双向循环链表时,因为约瑟夫环的大小由输入决定。
为方便操作,我们将每个结点的数据域的值定为生成结点时的顺序号和每个人持有的密码。
进行操作时,用一个指针current指向当前的结点,指针front始终指向头结点。
然后建立双向循环链表,因为每个人的密码是通过rand(>
函数随机生成的,所以指定第一个人的顺序号,找到结点,不断地从链表中删除链结点,直到链表剩下最后一个结点,通过一系列的循环就可以解决改进约瑟夫环问题。
1.2设计平台
Windows2000以上操作系统;
MicrosoftVisualC++6.0
2概要设计
已知n个人<
以编号1,2,3...n分别表示)围成一圈。
从编号为1的人开始报数,数到m的那个人出列;
他的下一个人又从1开始报数,数到m的那个人又出列;
依此规律重复下去,直到一圈的人全部出列。
这个就是约瑟夫环问题的实际场景,有一种是要通过输入n,m,k三个正整数,来求出列的序列。
这个问题采用的是典型的循环链表的数据结构,就是将一个链表的尾元素指针指向队首元素。
link=head。
解决问题的核心步骤:
首先建立一个具有n个链结点,无头结点的循环链表。
然后确定第1个报数人的位置。
最后不断地从链表中删除链结点,直到链表为空。
改进的约瑟夫环问题与原问题思路一致,只是不再采用单循环链表存储结构,而采用双向循环链表,而且用一个判断语句来决定报数的方向的顺时针还是逆时针。
本课程设计主要采用了类的数据结构,程序中包含了两个类:
Linklist,Joseph。
2.1类LinkList
主要功能是创建结点,每个结点数值域包括data,password,还有指示前驱结点的指针llink,和指示后继结点的指针rlink。
2.2类Joseph
主要功能是实现创建双向循环链表及一些相应的操作。
2.3类异常处理
在C++程序中,可以使用try-throw-catch结构处理程序异常。
采用这一程序结构能够将使用和实现分离:
类和函数的实现者使用throw语句易地错误类别通知使用者。
使用者根据获悉的错误类别采取相应的措施,这就是异常处理。
3详细设计和实现
改进约瑟夫环问题的基本思路和原问题基本一致,只是一个采用单循环链表,另一个采用双向循环链表来解决问题。
第一步是定义结构变量结点linklist,并在该结点下定义结点的元素域:
data,password,指针域:
lLink和rLink。
然后建立一个由n个链结点,无表头结点的双向循环链表。
并由构造函数对结点赋值,由随机函数rand(>
产生每个结点的password。
由于每个结点的password是由随机函数产生的,也就是每个结点的password是后知的,所以在一开始人为地指定一个结点的顺序,由此结点开始报数。
报password个数后,报到的那个结点被删除,它的password被记录下,由它的下一个结点开始逆方向报数………如此循环,直到循环链表里只剩下一个结点,那就是问题所求的结果。
具体到问题上,还需要创建一个Joseph类,由构造函数来初始化,输入所有的人数,也就是表长,然后指定由第几个人开始报数。
在Joseph类中定义一个GetWinner(>
函数,由它来实现获得最后的胜利者。
并在该类中设置一个判断语句来确定先由顺时针报数并淘汰了一个人之后,再按逆时针顺序报数,如此交替进行。
3.1创建结点Node
链表都是由一个个结点组成,由于结点的不同,组成的链表也不同。
因此需要创建双向链表结点。
由于每一个结点有一个密码和一个序号,所以可以将结点结构体定义为:
structNode{
intdata。
intpassword。
DNode*llink。
DNode*rlink。
}图3.1结点Dnode
3.2创建双向循环链表
创建一个空双向循环链表,双向循环链表和每个结点包括三个域:
Element,
lLink,rLink.其中element为元素域,rLink域为指向后继结点的指针,新增的lLink域用以指向前驱结点。
双向链表也可以带表头结点,并且也可以构成双向循环链表。
此时,表头结点的rLink,lLink分别指向双向循环链表的头结点(或表头结点>
和尾结点。
一个结点的lLink域的指针指向它左边结点的后部,这并不意味着该结点的lLink域保存的仍是该左边结点存储块的起始地址。
在此处,指针指向某个结点任何部分都是等价的,都是指该存储块的起始位置。
图3.2单表头的双向循环链表
每当结点计数到某一结点时,将他的前驱结点接到他的后继结点,然后将他的密码值password赋给计数变量,再将此结点删除。
如此循环下去,直到最后变为空的单循环链表为止。
由于当某个人退出圆圈后,报数的工作要从下一个人开始继续,剩下的人仍然是围成一个圆圈的,可以使用循环表,由于退出圆圈的工作对应着表中结点的删除操作,对于这种删除操作频繁的情况,选用效率较高的链表结构,为了程序指针每一次都指向一个具体的代表一个人的结点而不需要判断,链表不带头结点。
所以,对于所有人围成的圆圈所对应的数据结构采用一个不带头结点的循环链表来描述。
设头指针为front,front始终指向头结点,并定义指针current记录当前的结点。
并根据具体情况移动<
顺逆时针)。
为了记录退出的人的先后顺序,采用一个顺序表进行存储。
程序结束后再输出依次退出的人的编号顺序。
由于只记录各个结点的data值就可以。
最后通过函数调用来输出顺序。
要解决约瑟夫环问题,首先一点就是必须有一个环,所以第一步我们必须建立一个双向循环链表。
而建立一个双向循环链表必须有一个空的双向循环链表,然后运用尾插法建立一个双向循环链表,这样约瑟夫环就创建出来了,接下来就是处理约瑟夫环问题。
3.3从链表中删除结点
在双向循环链表中,一个结点的前驱结点地址保存在该结点的lLink域中,这样可以方便地实现在一个指定结点之前插入一个新结点的操作,也可以方便地删除某个指定结点。
函数通过代码:
q->
llink->
rlink=q->
rlink。
rlink->
llink=q->
llink。
deleteq。
来删除当前的那个结点q,通过循环来一次次删除当前的结点,直到链表中剩下最后一个结点。
具体程序如下:
#include<
stdio.h>
malloc.h>
stdlib.h>
typedefstructnode//定义单循环链表中节点的结构
{
intnum。
//序列号即个人的编号
intcipher。
//个人所持有的密码
structnode*next。
}linklist。
classYSFH
public:
linklist*Creat(intn>
。
linklist*Select1(intm>
linklist*head。
//头指针指示有n个结点的单循环链表creat
protected:
linklist*Select(linklist*head,intm>
private:
linklist*p。
//存放人员信息
linklist*r。
//临时存放
linklist*q。
intk。
}。
/*建立单循环链表函数*/
linklist*YSFH:
:
Creat(intn>
p=(linklist*>
malloc(sizeof(linklist>
>
head=p。
num=1。
printf("
随机产生第1个人的密码:
"
cipher=rand(>
%10。
{if(p->
cipher==0>
p->
}
%d\n"
p->
cipher>
r=p。
next=p。
for(intk=2。
k<
=n。
k++>
{
p=(linklist*>
num=k。
//给每人一个编号
printf("
随机产生第%d个人的密码:
k>
{if(p->
r->
r=p。
next=head。
return(head>
}
/*决定出列编号*/
Select1(intm>
returnSelect(head,m>
Select(linklist*head,intm>
q=head。
k=1。
p=q->
next。
//q为p的前驱指针,p指向当前报数的人
出列的序号依次为:
"
//在head中的第一个结点起循环记数找第m个结点
while(q!
=p>
k=k+1。
//报一次数
if(k%m==0>
//所报数等于报数上限值时
{
printf("
%d"
num>
//输出该结点的num值
m=p->
cipher。
//把该结点的cipher(密码>
值赋给m
q->
next=p->
//对应的节点从链表中删除
free(p>
k=0。
p=q->
}
else{
q=p。
p=p->
//p指向当前报数的人
voidmain(>
intn,m。
m!
=0。
YSFHY。
输入总人数n:
scanf("
%d"
&
n>
Y.head=Y.Crea