输出形式:
中文提示按照m个猴子,数n个数的方法,输出为大王的猴子是几号,建立一个函数来实现此功能。
3、需求分析:
①输入数据m,n
②计算出最终猴子大王的序号。
③模拟出整个过程。
④找到合适的数据结构处理这个问题。
⑤找到正确的方法解决这个问题。
二、概要设计
对题意进行分析后,可以画出整个过程的流程图。
具体流程图:
这个问题属于约瑟夫环问题,我们对这个题目进行具体分析:
假如现在m=5,n=2,即有5只猴子,按照循环数2的方法,我们演变这个过程:
第一次:
12345
×2号出局
第二次:
12345
××4号出局
第三次:
12345
×××1号出局
第四次:
12345
××××5号出局
最后得到猴子大王的序号是3号。
那么一般化,对于m猴子,n只猴子我们该怎么做?
三、详细设计
1、循环队列
队列是一种特殊的线性表,它只允许在表的前端(front)进行删除操作,而在表的后端(rear)进行插入操作。
进行插入操作的端称为队尾,进行删除操作的端称为队头。
队列中没有元素时,称为空队列。
仔细分析整个过程,我发现这个过程和学过的循环队列特别像,在学习循环队列的时候,如果采取数组来存储队列,那么在front端弹出元素就执行front=(front+1)%MAX,在rear端进入队列的时候就执行rear=(rear+1)%MAX,以实现绕圈的操作。
回顾一下循环队列的具体的入队算法:
1、tail=tail+1;
2、若tail=n+1,则tail=1;
3、若head=tail尾指针与头指针重合了,表示元素已装满队列,则作上溢出错处理;
4、否则,Q(tail)=X,结束(X为新入出元素)。
队列空的条件:
front==rear
队列满的条件:
(rear+1)%=MAXSIZE==front
那么在这个问题中能不能学习这个技巧?
仔细想想,我认为答案是可以的。
在问题中还牵涉到一个淘汰的过程,对于这个问题,我们该怎么处理?
其实这个问题比较好处理,如果我们采取的是链表那么直接删除掉就可以了,如果是数组,我们只要把删除的做个标记,下次碰到已经删除的直接跳过这个元素就可以了。
2、循环链表
在这个问题上也可以用链表。
链表是一种常见的基础数据结构,是一种线性表,但是并不会按线性的顺序存储数据,而是在每一个节点里存到下一个节点的指针(Pointer)。
由于不必须按顺序存储,链表在插入的时候可以达到O
(1)的复杂度,比另一种线性表:
顺序表快得多,但是查找一个节点或者访问特定编号的节点则需要O(n)的时间,而顺序表相应的时间复杂度分别是O(logn)和O
(1)。
使用链表结构可以克服数组链表需要预先知道数据大小的缺点,链表结构可以充分利用计算机内存空间,实现灵活的内存动态管理。
但是链表失去了数组随机读取的优点,同时链表由于增加了结点的指针域,空间开销比较大。
在计算机科学中,链表作为一种基础的数据结构可以用来生成其它类型的数据结构。
链表通常由一连串节点组成,每个节点包含任意的实例数据和一或两个用来指向明上一个/或下一个节点的位置的地址。
链表最明显的好处就是,常规数组排列关联项目的方式可能不同于这些数据项目在记忆体或磁盘上顺序,数据的存取往往要在不同的排列顺序中转换。
而链表是一种自我指示数据类型,因为它包含指向另一个相同类型的数据的指针(链接)。
链表有很多种不同的类型:
单向链表,双向链表以及循环链表。
显然这个问题可以用循环链表处理。
循环链表是与单链表一样,是一种链式的存储结构,所不同的是,循环链表的最后一个结点的指针是指向该循环链表的第一个结点或者表头结点,从而构成一个环形的链。
循环链表的运算与单链表的运算基本一致。
所不同的有以下几点:
1、在建立一个循环链表时,必须使其最后一个结点的指针指向表头结点,而不是象单链表那样置为NULL。
此种情况还使用于在最后一个结点后插入一个新的结点。
2、在判断是否到表尾时,是判断该结点链域的值是否是表头结点,当链域值等于表头指针时,说明已到表尾。
而非象单链表那样判断链域值是否为NULL。
回顾下链表删除的过程:
如图所示,那么删除q节点的操作即为:
p->next=q->next;
deleteq;
3、伪代码
讨论了上述问题之后,那么接下来就是要来模拟这个过程了。
以伪代码的形式进行这个操作:
第一步,进行1-n的报数(在这个过程中如果遇到已经出局的猴子,就跳过)。
第二步,对第n只猴子进行删除操作(删除或者标记)。
第三步,找到下一只将要报数的猴子。
第四步,如果只剩下一只猴子就执行第五步操作,不是就跳到第一步继续执行。
第五步,输出所求猴子大王的序号。
4、具体函数分析及变量分析:
main()主程序,包括输入输出和主要函数的调用。
Link_solve()用链表解决这个问题的方法,具体实现看附录。
Array_solve()用数组解决这个问题的方法,具体实现看附录。
m:
总猴子数
n:
报数循环的n
ans1:
用链表得到的答案
ans2:
用数组得到的答案
其他函数中变量在附录中均有介绍。
四、调试分析和测试结果
对上述程序进行多次数据测试,其结果分别为:
当猴子总数为5,每次数2个的方法:
当猴子总数是6,每次数4个的方法:
当猴子总数为20,每次数7只方法:
当猴子总数为11,每次数3只方法:
通过对上述数据手工的模拟及测试,发现结果均符合实际过程。
五、总结
短短一周的课程设计时间很快就过去了,而我所选的“猴子选大王”设计题目也接近尾声。
这期间,有对自己学过的知识的一个回顾,也有新的知识的补充。
当有自己不懂时就翻阅资料,寻求解答;当有疑问的时候,有成员之间的讨论,老师的指导。
经过磕磕碰碰总算完了任务。
这是我们一个星期以来的努力成果,同时是这学期学数据结构的总结,是对自己能力的考验。
在课程设计的过程当中,是对平时学习的检测,但在真正设计起程序来,很多困难还是意想不到的,这也告诉我,书上的知识是远远不够的,要想学到更多的知识,需要在实践过程中不断的摸索,在摸索中不断提升自己。
一周课程设计的时间是过去了,但是,对数据结构的学习似乎才是开始,以后要学习的还很多很多,前面要走的路还很远很远。
而我也要整装待发,在摸索中前进,在前进中不断摸索,让自己的路走得更远更长!
六、参考文献
1、严蔚敏、吴伟民、米宁.《数据结构题集(C语言版)》.清华大学出版社
2、赵逢禹、罗道昆、路玲、杜光耀.《数据结构与C语言高级程序设计》.北京航空航天大学出版社
3、严蔚敏、吴伟民.《数据结构(C语言版)》.清华大学出版社.2010年
4、谭浩强.《C语言程序设计》(第2版).清华大学出版社.2008年
5、梁旭、谷晓琳、黄明.《C语言课程设计》(第2版).电子工业出版社.2009年
6、XX文库
7、XX文库
七、附录
详细代码:
#include
usingnamespacestd;
intm,n,ans1,ans2;
bool*flag;
structNode{
intnum;//存储所在的序号
structNode*next;//指向下一个节点
};
//循环链表处理这个问题
intLink_solve()
{
Node*head=newNode,*p,*q;//head为头节点,p,q为临时变量。
head->num=1;
inti;
p=head;
for(i=2;i<=m;i++)
{
q=newNode;
q->num=i;
p->next=q;
p=q;
}
p->next=head;//形成环
q=p=head;//开始报数
intt=1;//统计循环报数的次数
while(t{
for(i=2;i<=n;i++)//报数
{
q=p;
p=p->next;
}
//找到了出局的猴子,并输出。
cout<<"第"<"<num<<"号。
"<q->next=p->next;//删除掉出局的猴子
deletep;//清除空间
p=q->next;
}
cout<returnp->num;//返回最后剩下的那只猴子序号
}
//循环数组处理这个问题
intArray_solve()
{
inti,p=0;
intt=1;//统计循环报数的次数
while(t{
for(i=1;i{
p=(p+1)%m;//形成循环数数
//当碰到已经出局的,跳过
while(flag[p])
p=(p+1)%m;
}
flag[p]=1;
cout<<"第"<"<
"<//当碰到已经出局的,跳过,找到下一只没出局的猴子
while(flag[p])
p=(p+1)%m;
}
//获取剩下的最后的一只猴子序号
while(flag[p])
p=(p+1)%n;
cout<returnp+1;
}
intmain()
{
cout<<"输入总猴子数:
";
cin>>m;//输入猴子数
cout<<"输入每次数的个数:
";
cin>>n;//输入循环报数的方式
flag=newbool[m];//标记猴子是否出局,出局的是1,没出局的是0
memset(flag,0,sizeof(bool)*m);//初始化猴子全部未出局
cout<<"链表处理:
"<ans1=Link_solve();//链表处理得到答案
cout<<"按照"<"<cout<<"数组处理:
"<ans2=Array_solve();//数组处理得到答案
cout<<"按照"<"<return0;
}