敢死队问题课程设计剖析
2015年C语言程序课程设计
学院:数学与信息科学学院
专业:数学与应用数学
班级: 2014级1班
姓名:王杰
课题名称:敢死队问题
指导老师:覃美珍老师
2015年12月30日
单循环链表结构
●需求分析
1.本程序输入队伍人数M为任意的,最终输出记数的初始位置,首先报数上限为5,当达到报数上限时,那名士兵出列执行任务,从下个人开始记数,再次循环,直到只剩一人,得到其在队伍中的位置,通过数学思想求得题目要求即队长为首的情况下需要记数初始位置。
2.程序执行的命令包括:
(1)构造链表;(2)数据输入;(3)执行删除;(4)输出要求数值
(5)结束。
●概要设计
以单循环链表为存储结构,包含三个模块:
1.主程序模块
2.构造链表并初始化
3.删除
详细设计
#include
using namespace std;
typedef struct node
{
int num;
node *next;
}lnode;//定义一个结构体,num表示士兵的编号
void main()
{
int M;//士兵个数
int i;
int start,count;
lnode*l;
cout<<"input the value of M:";
cin>>M;
for(start=1;start<=M;start++)//start表示从哪个士兵开始
{
lnode*p=new lnode;//分配空间
l=p;//l为始终指向第一个节点的指针
l->num=1;//第一个节点代表的士兵的编号赋值为1
for(i=2;i<=M-1;i++)
{
lnode*q=new lnode;
q->num=i;
p->next=q;//创建其他节点,并将它接到链表尾部
p=q;//把指针p移一位,使他指向刚链上的节点
}
lnode*t=new lnode;//从这里开始创建最后一个节点
t->num=M;//节点编号为M
p->next=t;
p=t;
t->next=l;//并将最后一个节点与第一个节点相连,变成循环链表
p=l;//p指向第一个节点
for(i=1;i { p=p->next; }//一个一个往下找,一直找到编号为start的那个节点,从这里开始计数 count=0;//删除节点个数 while(count { for(i=1;i<5;i++) { t=p; p=t->next; }//数到5就停下 t->next=p->next;//删除该节点 count++; p=t->next;//从下个节点开始继续数 }//当删除节点为M-1个时,结束 if(p->num==1) break; } cout<<"start from:"< } 调试分析 调试结果: 算法分析: 在程序设计前,如果按原题所设,则需设队长为第一位,再分别测试从第几个开始才能符合条件。现在改变思想,通过数学思想:单循环链表本身是一个循环体,可先求出当从第一个出发的话,求出每隔5个出去一个,剩下最后一个再令他与1号(排长)对比,如果是1则符题目要求,输出结果。 ●总结 通过这次课程设计我又学到了很多东西,如程序的模块化设计思想,同时也加深了对数据结构这门课程的理解和学会了如何在实际中应用数据结构. 这个方法是用单循环链表来做的,实现的方法是这样的:首先从第一号开始报数,循环到指定的偏移位置删除结点,直至剩下一个结点。然后设计输出,如果最后一个为队长,则第一个报数的人的编号即为所求,输出,结束。 循环队列结构 ●需求分析 本程序输入队伍人数M为任意的,最终输出记数的初始位置,首先知道报数上限为5,当达到报数上限时,那名士兵前面的人出列后按顺序插到对尾,之后该士兵出列执行任务,从下个人开始记数,再次重复,直到只剩一人,得到其在队伍中的位置,通过数学思想求得题目要求即队长为首的情况下需要记数初始位置。 程序执行的命令包括: (1)构造队列;(2)数据输入;(3)执行删除;(4)输出要求数值 (5)结束。 程序测试 当输入的总数是10时,输出结果应该是从第9个战士开始计数。 概要设计 本程序其实质是约瑟夫环问题,本次实验用了循环队列结构,并运用模块化的程序设计思想,算法的实现是这样的: 1. 定义结构体类型 2.定义变量并初始化 3.队列循环模块初始化 4.判断队列是否满 5.满足题目条件,输出 详细设计 模块的分析: #include using namespace std; #define maxlen 100 //假定预分配的队列空间最多为100个元素 typedef struct { int data[maxlen]; int front; //代表数组存放的第一个数据对应的下标,即对头 int rear; //代表数组存放的最后一个数据对应的下标,对尾 int count; //计数器,记录队中元素总数 }CirQueue; // 初始化对令队为空 void queue(CirQueue *Q) { Q->front=Q->rear=0; //对头等于对尾代表队列为空 Q->count=0; } // 判队列是否为空 int empty(CirQueue *Q) { if(Q->count==0) return 1; //如果队里的元素个数为0,队列空,返回1 else return 0; } //判队列是否满 int full(CirQueue *Q) { if(Q->count==maxlen) return 1; else return 0; } //将元素进队 void append(CirQueue *Q,int x) { if (full(Q)) { cout<<"列满\n"; exit(1); }//如果堆满就退出程序 Q->count ++;//否则就进入,元素总数加一 Q->data[Q->rear]=x; //数组的最后一位赋值为x Q->rear=(Q->rear+1)%maxlen; } //元素出队列 int serve(CirQueue *Q) { int temp; if(empty(Q)) { cout<<"队列为空\n"; //下溢,退出运行 exit(1); } temp=Q->data[Q->front]; //将队头的元素取出到temp里面 Q->count--; //队列元素个数减1 Q->front=(Q->front+1)%maxlen; //循环意义下的头指针后移(即数组中第一个元素存放的位置的下标加1) return temp; } void main() { int M,i,start,count,j; CirQueue s; cout<<"input the value of M :"; cin>>M; for(start=1;start<=M;start++)//start为测试起点 { queue(&s); for(i=1;i<=M;i++) { append(&s,i); } //将所有士兵的编号依次进队 for(i=1;i { j=serve(&s); append(&s,j);//这两句是把对头的元素取出来然后放到对尾去,经过这个循环以后,要从那个开始的士兵的编号就会在队的对头位置了 } count=0; //删除结点数 while(count { for(i=1;i<5;i++) { j=serve(&s); append(&s,j); //这两句是把对头的元素取出来然后放到对尾 } //经过这个循环以后,对头的那个节点的值就是数到5对应的节点 j=serve(&s);//把这个节点出队,但是没有在把它放到对尾,就相当于把它删除了 count++; }//同第一个方法是一样,删除点数为m-1是结束循环 if(s.data[s.front]==1) break; //此时队只剩一个点,如果是排长就输出} cout<<"start from :"< } ●调试结果 系统界面 算法分析 本次采用循环队列的数据结构方法实现,但其先进先出的算法结构加大了本程序的时间及空间复杂度。 ●总结 这个方法是用循环队列来做的,实现的方法是这样的:首先从第一号开始报数,循环到指定的偏移位置其前面节点出对后插到队尾,该结点出对即删除,直至剩下一个结点。然后设计输出,令这个位置为队长位置,队首为开始报数的位置,并按此输出即为所求。 体会与感受 在程序的编写中不能一味得向已有的程序进行模仿,而要自己去摸索,去寻求最好的解决方式,只有带着问题去反复进行实践,才能更熟练的掌握和运用,当然,对现有的程序也要多去接触,因为有些程序是我们无法在短时间内想出来的.最重要的一点就是要持之以恒,要经常性的复习原来所接触的程序,这样才能保证我们有足够的经验去面对程序问题。