敢死队问题说明书
*******************
实践教学
*******************
兰州理工大学
计算机与通信学院
2012年春季学期
算法与数据结构课程设计
题目 : 敢死队问题
专业班级: 信息与计算科学一班
姓名 : 段永超
学号 : 10500110 指导教师: 张永
成绩 :
摘要 (3)
1.序言 (4)
2.采用类c语言定义相关的数据类型 (5)
3.各模块的伪码算法 (6)
4、函数的调用关系图 (9)
5.调试分析 (13)
6.测试结果 (15)
7.源程序(带注释) (18)
总结 (21)
参考文献 (22)
致谢 (23)
附件Ⅰ源程序代码 (24)
随着计算机技术的发展,计算机领域取得了很大的研究成果,其应用与人们的日常生活息息相关。尤其在我们的娱乐方面,带来了很大的改革,极大的丰富了我们的生活。敢死队就是一种很好的诠释。
敢死队问题是基于Microsoft Visual C++ 6.0,通过算法与数据结构的分析,用c语言编程实现,本程序输入队伍人数M为任意的,最终输出记数的初始位置,首先报数上限为5,当达到报数上限时,那名士兵出列执行任务,从下个人开始记数,再次循环,直到只剩一人,得到其在队伍中的位置,此程序设计主要有:构建链表、数据输入、执行删除、输出要求数值及结束几部分构成。采用循环单链表及循环队列解决此问题。
关键词:vc++ 6.0 算法单链表队列
1.序言
有M个敢死队员要炸掉敌人的一碉堡,谁都不想去,排长决定用轮回数数的办法来决定哪个战士去执行任务。如果前一个战士没完成任务,则要再派一个战士上去。现给每个战士编一个号,大家围坐成一圈,随便从某一个战士开始计数,当数到5时,对应的战士就去执行任务,且此战士不再参加下一轮计数。如果此战士没完成任务,再从下一个战士开始数数,被数到第5时,此战士接着去执行任务。以此类推,直到任务完成为止。
用循环单链表及循环队列两种不同的数据结构实现基本操作,排长不愿意去,假设排长为1号,请设计程序,求出从第几号战士开始计数才能让排长最后一个留下来而不去执行任务。此程序主要有四个功能模块,包括三个解决敢死队问题方案的模块和一个退出系统模块。三个解决方案分别采用了循环聊表储存结构、线性表储存结构、循环队列储存结构。
1)循环链表:创建循环链表及对链表的删除来实现。
2)线性表:对线性表的构建及相关的函数的操作来完成。
3)循环队列:进行队列的进队与出对操作完成设计。
2.采用类c语言定义相关的数据类型
采用c语言进行相关数据类型的定义:
循环链表:
typedef struct node
{
int data;
struct node *next;
}LNode;/* 定义结点类型 */
线性表:
typedef struct KList /*定义数据结构体类型*/
{
ElemType *elem; /*存储空间基址*/
int length; /*当前长度*/
int listsize; /*当前分配的存储容量(以sizeof(ElemType)为单位)*/ }SqList;
typedef struct
{
int data[QueueSize];
int front;
int rear;
int count; //计数器,记录队中元素总数
}CirQueue;
主函数相关定义:
SqList L;
int s,i,m,count=0; /*声明变量*/
LNode *p;
int z,y;
int num;
int opt;
3.各模块的伪码算法
循环单链表伪码算法:
typedef struct node
{
int data;
struct node *next;
}LNode;/* 定义结点类型 */
LNode* CREAT(int n)
{ /* 创建循环链表 */
LNode *s,*q,*T;
if(n!=0)
{
T=q=(LNode *)malloc(sizeof(LNode));
q->data=1;/* 生成第一个结点并使其data值为1 */ for(i=2;i<=n;i++)
{
s=(LNode *)malloc(sizeof(LNode));
q->next=s;
q->next->data=i;/*赋值*/
q=q->next;
}
q->next=T;
}
return T;
}
DELETE (LNode* T,int m)/* 链表的删除 */{
LNode *a;int i;
while (T->next!=T)
{
for (i=1;i T=T->next; a=T->next; T->next=a->next; free(a) T=T->next; } return (T->data); } 线性表伪码算法: typedef struct KList /*定义数据结构体类型*/ { ElemType *elem; /*存储空间基址*/ int length; /*当前长度*/ int listsize; /*当前分配的存储容量(以sizeof(ElemType)为单位)*/ }SqList; int InitList_Sq( SqList &L) /*创建线性表函数*/ { L.elem=(ElemType *)malloc(LIST_INIT_SIZE * sizeof(ElemType)); if(!L.elem) { printf("存储分配失败"); return ERROR; } else { L.length=0; /*空表长度为0*/ L.listsize=LIST_INIT_SIZE; return OK;/*初始存储容量*/ } } int ListInsert_Sq(SqList &L) /*线性表再分配函数*/ { /*SqList L;*/ int *newbase; newbase=(ElemType *)realloc(L.elem, (L.listsize+LISTINCCREMENT)*sizeof(ElemType)); /*为顺序表增加一个大小为存储LISTINCCREMENT个数据元素的空间*/ if(!newbase) {printf("存储分配失败"); return ERROR;} else { L.elem=newbase; /*新基址*/ L.listsize+=LISTINCCREMENT; /*增加存储容量*/ return OK; } } 循环队列伪码算法: #define QueueSize 1000 //假定预分配的队列空间最多为1000个元素typedef struct{ int data[QueueSize]; int front; int rear; int count; //计数器,记录队中元素总数 }CirQueue; void Initial(CirQueue *Q) {//将顺序队列置空 Q->front=Q->rear=0; Q->count=0; //计数器置 } // 判队列空 int Empty(CirQueue *Q) { return Q->front==Q->rear; } //判队列满 int Full(CirQueue *Q) { return Q->rear==QueueSize-1+Q->front; } //进队列 void EnQueue(CirQueue *Q,int x) { if (Full(Q)) { printf("队列上溢"); //上溢,退出运行 exit(1); } Q->count ++; //队列元素个数加 Q->data[Q->rear]=x; //新元素插入队尾 Q->rear=(Q->rear+1)%QueueSize; //循环意义下将尾指针加 } //出队列 int DeQueue(CirQueue *Q) { int temp; if(Empty(Q)) { printf("队列为空"); //下溢,退出运行 exit(1); } temp=Q->data[Q->front]; Q->count--; //队列元素个数减 Q->front=(Q->front+1)%QueueSize; //循环意义下的头指针加1 return temp; } 4、函数的调用关系图 系统结构图 图 1 总体模块图 循环单链表: 以单循环链表为存储结构,包含三个模块: 主程序模块包含敢死队人数的输入,死亡数字的输入,函数的调用,结果的输出。 构造链表并初始化 构造链表,给每个结点赋值,给队员编号。 删除 当报数到死亡数字时队员出列去执行任务,删除该节点。 流程图如下: 图 2 循环单链表流程图1、线性表存储: 定义变量并初始化 线性表初始化 当队员数小于等于1时,输出结果 流程图如下: 图 3 线性表流程图 2、循环队列: 这个方法是用队列循环来做的,实现的方法是这样的:首先从第一号开始报数,循环到指定的偏移位置删除结点,直至剩下一个结点。然后再比较一下它的号码是不是等于1,如果等于则输出开始计数位置,如果不等,继续循环查找,直到找出符合条件的计数起始位置,输出结果。 图 4 循环队列流程图 主函数主要对输出、输入函数的运用,对循环单链表的创建和删除操作;对循环队列的入队及出对的操作,及对线性表的有关操作。实现了对敢死队问题的设计。 图 5 函数调用关系图 5.调试分析 在程序设计前,如果按原题所设,则需设队长为第一位,再分别测试从第几个开始才能符合条件。现在改变思想,通过数学思想:单循环链表本身是一个循环体,可先求出当从第一个出发的话,求出每隔5个出去一个,剩下最后一个再令他与1号(排长)对比,如果是1则符题目要求,输出结果。 采用循环队列的数据结构方法实现,但其先进先出的算法结构加大了本程序的时间及空间复杂度。 线性表结构运用模块化设计思想实现。 a、调试中遇到的问题及对问题的解决方法 在调试中遇到很多语法问题,大多都是因为粗心导致,细心查看就可以解决了,例如忘记分号结尾,输入法差异等等;下面主要介绍一个困惑很久的问题:在VC中同时兼容C语言和C++语言,当两种语言和在一起时,某些语法存在优先级问题,例如最简单的输入与输出,都会因为优先级不同而是程序不按理论上的顺序进行执行。解决此问题我花费了大量时间查阅资料,查看了好多相关论文,最终发现是优先级导致的问题。 b、算法的时间复杂度和空间复杂度。 线性表的时间复杂度为O(n),时间复杂度为 O(L.listsize+LISTINCCREMENT) 循环单链表的时间复杂度为O(n2),空间复杂度为O(n) 循环队列的时间复杂度为O(1), 空间复杂度为O(QueueSize) 6.测试结果 1、进入用户主界面,选择合适的方法: 图 6 主界面图 2、以10个队员,死亡数字为5来进行运行 图 7 循环存储图 3、选择第二项功能,实现线性表运算功能 图 8 线性表存储图4、选择第三项功能,用循环队列实现结果 图 9 循环队列存储图 5、退出系统 图 10 退出系统图 7.源程序(带注释) switch(opt) { case 1: system("cls"); printf("您使用是循环链表储存结构\n\n"); efg: printf("请输入敢死队的人数:\n"); scanf("%d",&num); if(num<1) { printf("输入有误请重新输入\n"); goto efg; } printf("请输入死亡数数字\n"); m: scanf("%d",&m); if(m>num || m<1) { printf("输入有误请重新输入"); goto m;} else { p=CREAT(num); y=DELETE(p,m); z=num-y+2; if(z%num==0) printf ("从第 %dth:开始计数\n",z); else printf("从第%d号战士开始计数才能让排长最后一个留下来而不去执行任务。\n",(num-y+2)%num);} break; case 2: system("cls"); printf("您使用的是线性表储存结构\n\n"); e: printf("请输入敢死队的人数:\n"); scanf("%d",&num); if(num<1) { printf("输入有误请重新输入\n"); goto e; } m=5; InitList_Sq(L); while(num>L.listsize ) /*当顺序表当前分配的存储空间大小不足时进行再分配*/ ListInsert_Sq(L); for(i=0;i s=num; i=0; while(s>1) /*当所剩敢死队员总数为1时,总循环结束*/ { for(i=0;i if(L.elem[i]!=0) { count++; if(count==5) /*报数循环*/ { L.elem[i]=0; /*表示队员出列*/ count=0; /*计数器清零*/ s--; /*敢死队员数-1*/ } } } for(i=0;i if(L.elem[i]!=0) if((num-L.elem[i]+2)%num==0) printf("从第%d号战士开始计数才能让排长最后一个留下来而不去执行任务。\n",num); else printf("从第%d号战士开始计数才能让排长最后一个留下来而不去执行任务。\n",(num-L.elem[i]+2)%num); break; case 3: system("cls"); printf("您使用的是循环队列储存结构\n\n"); int start,count,j; CirQueue s; g: printf("请输入敢死队的人数:\n"); scanf("%d",&num); if(num<1) { printf("输入有误请重新输入\n"); goto g; } for(start=1;start<=num;start++)//start为测试起点 { Initial(&s); for(i=1;i<=num;i++) { EnQueue(&s,i); } for(i=1;i { j=DeQueue(&s); EnQueue(&s,j); } count=1; while(count { for(i=1;i<5;i++) { j=DeQueue(&s); EnQueue(&s,j); } j=DeQueue(&s); count++; } if(s.data[s.front]==1) break; } printf("从第%d号战士开始计数才能让排长最后一个留下来而不去执行任务。\n",start); break; case 4:exit(0); } goto abc; }