敢死队问题+数据结构课程设计.docx
《敢死队问题+数据结构课程设计.docx》由会员分享,可在线阅读,更多相关《敢死队问题+数据结构课程设计.docx(16页珍藏版)》请在冰豆网上搜索。
敢死队问题+数据结构课程设计
一.问题描述
二.线性表数据结构
(一).需求分析
(二).概要设计
(三).详细设计
(四).程序调试及运行
(五).设计总结
(六).附件
三.单循环链表数据结构
(一).需求分析
(二).概要设计
(三).详细设计
(四).程序调试及运行
(五).设计总结
(六).附件
一.问题描述
敢死队问题
有M个敢死队员要炸掉敌人的一碉堡,谁都不想去,排长决定用轮回数数的办法来决定哪个战士去执行任务。
如果前一个战士没完成任务,则要再派一个战士上去。
现给每个战士编一个号,大家围坐成一圈,随便从某一个战士开始计数,当数到5时,对应的战士就去执行任务,且此战士不再参加下一轮计数。
如果此战士没完成任务,再从下一个战士开始数数,被数到第5时,此战士接着去执行任务。
以此类推,直到任务完成为止。
排长是不愿意去的,假设排长为1号,请你设计一程序,求出从第几号战士开始计数才能让排长最后一个留下来而不去执行任务。
要求:
至少采用两种不同的数据结构的方法实现。
二.线性表数据结构
(一)、需求分析
1.本程序输入队伍人数n为任意的,最终输出记数的初始位置,首先输入一个报数上限m,当达到报数上限时,那名士兵出列执行任务,从下个人开始记数,再次循环,直到只剩一人,得到其在队伍中的位置,通过数学思想求得题目要求即队长为首的情况下需要记数初始位置。
2.程序执行的命令包括:
(1)构造数据结构;
(2)数据输入;(3)执行队员出列;(4)输出要求数值
(5)结束。
3.数据测试:
当n=10输出结果为:
要求的位置是:
9
(二)、概要设计
算法思想:
本程序其实质是约瑟夫环问题,本次实验用了线性表和循环队列两种数据结构,并运用模块化的程序设计思想,算法的实现是这样的:
1.定义结构体类型
2.定义变量并初始化
3.线性表初始化
4.队员报数,是5的倍数出列
5.当队员数等于1时,输出
程序框图:
开始
声明数据类型
定义变量并初始化
初始化线性表
输入敢死队员总数
敢死队员人数>线性表长度
队员报数
报数值=偏移值?
队员出列即[i]清零
剩下的队员数>1?
输出
增加存储分配
(三)、详细设计
宏定义:
#defineLIST_INIT_SIZE100
#defineLISTINCCREMENT10
数据类型定义:
typedefintElemType;
定义数据结构:
typedefstructKList/*定义数据结构体类型*/
{
ElemType*elem;/*存储空间基址*/
intlength;/*当前长度*/
intlistsize;/*当前分配的存储容量(以sizeof(ElemType)为单位)*/
}SqList;
模块一:
创建线性表函数
SqListInitList_Sq()/*创建线性表函数*/
{
SqListL;
=(ElemType*)malloc(LIST_INIT_SIZE*sizeof(ElemType));/**/
if(!
printf("Failinnewcreatlist"),exit(0);/*存储分配失败*/
else
{
=0;/*空表长度为0*/
=LIST_INIT_SIZE;/*初始存储容量*/
}
}
模块二:
线性表再分配函数
SqListListInsert_Sq(SqListL)/*线性表再分配函数*/
{
/*SqListL;*/
int*newbase;
newbase=(ElemType*)realloc,
+LISTINCCREMENT)*sizeof(ElemType));
/*为顺序表增加一个大小为存储LISTINCCREMENT个数据元素的空间*/
if(!
newbase)printf("Failinnewaddlist"),exit(0);/*存储分配失败*/
else
{
=newbase;/*新基址*/
+=LISTINCCREMENT;/*增加存储容量*/
}
}
主模块:
实现总体功能
main()
{
SqListL;
ints,i,m,count=0;/*声明变量*/
L=InitList_Sq();
printf("Pleaseinputthetataloftheteam:
");
scanf("%d",&m);/*输入敢死队员总数*/
while(m!
=0)/*当输入为0时退出程序*/
{
while(m>/*当顺序表当前分配的存储空间大小不足时进行再分配*/
L=ListInsert_Sq(L);
for(i=0;is=m;
i=0;
while(s>1)/*当所剩敢死队员总数为1时,总循环结束*/
{
for(i=0;iif[i]!
=0)
{
count++;
if(count==5)/*报数循环*/
{
[i]=0;/*表示队员出列*/
count=0;/*计数器清零*/
s--;/*敢死队员数-1*/
}
}
}
for(i=0;iif[i]!
=0)
if([i]+2)%m==0)/**/
printf("\nThewantedorderis%dth\n",m);
else
printf("\nThewantedorderis%dth\n",[i]+2)%m);
printf("Exitpleaseinput'0'OrGoon...\nPleaseinputthetataloftheteam:
");
scanf("%d",&m);/*输入敢死队员总数*/
}
}
思考:
本次设计问题的核心是如何输出,因为这影响到了程序的时间复杂度。
总模块的输出设计是这样实现的:
总是从第一个开始报数,报到5出列,直到剩下最后一个为止,然后就令这个位置为队长位置,队首为开始报数的位置,并按此设计输出即可。
这种方法大大的降低了时间复杂度,其复杂度为O(mn)。
(四)、程序调试及运行
程序调试过程中出现了下面的警告:
经查询错误为:
不可移动的指针(地址常数)赋值
最终发现是下面的定义错误造成的
在变量定义中int*newbase=0;
定义成了intnewbase=0;
经改正程序运行正常了.
数据测试如下:
时间复杂度为O(mn)
(五)、设计总结
通过这次课程设计我又学到了很多东西,如程序的模块化设计思想,同时也加深了对数据结构这门课程的理解和学会了如何在实际中应用数据结构编程。
我首先是用线性表来做的,开始的想法是想用试验的方法来查找到所要求的位置,即首先从第一号开始报数,然后检查最后剩下的一个是否是队首,然后从第二个开始报数……从第三个开始报数……直到所剩下的最后一个是队首。
虽然这种方法可以实现查找,可却是以消耗更多的时间为代价的,于是我又想到了这个方法:
总是从第一个开始报数,报到5出列,直到剩下最后一个为止,然后就令这个位置为队长位置,队首为开始报数的位置,并按此设计输出即可。
这种方法大大的降低了时间复杂度,复杂度为O(mn)。
(六)、附件
程序源代码:
#defineLIST_INIT_SIZE100
#defineLISTINCCREMENT10
typedefintElemType;
typedefstructKList/*定义数据结构体类型*/
{
ElemType*elem;/*存储空间基址*/
intlength;/*当前长度*/
intlistsize;/*当前分配的存储容量(以sizeof(ElemType)为单位)*/
}SqList;
SqListInitList_Sq()/*创建线性表函数*/
{
SqListL;
=(ElemType*)malloc(LIST_INIT_SIZE*sizeof(ElemType));/**/
if(!
printf("Failinnewcreatlist"),exit(0);/*存储分配失败*/
else
{
=0;/*空表长度为0*/
=LIST_INIT_SIZE;/*初始存储容量*/
}
}
SqListListInsert_Sq(SqListL)/*线性表再分配函数*/
{
/*SqListL;*/
int*newbase;
newbase=(ElemType*)realloc,
+LISTINCCREMENT)*sizeof(ElemType));
/*为顺序表增加一个大小为存储LISTINCCREMENT个数据元素的空间*/
if(!
newbase)printf("Failinnewaddlist"),exit(0);/*存储分配失败*/
else
{
=newbase;/*新基址*/
+=LISTINCCREMENT;/*增加存储容量*/
}
}
main()
{
SqListL;
ints,i,m,count=0;/*声明变量*/
L=InitList_Sq();
printf("Pleaseinputthetataloftheteam:
");
scanf("%d",&m);/*输入敢死队员总数*/
while(m!
=0)/*当输入为0时退出程序*/
{
while(m>/*当顺序表当前分配的存储空间大小不足时进行再分配*/
L=ListInsert_Sq(L);
for(i=0;is=m;
i=0;
while(s>1)/*当所剩敢死队员总数为1时,总循环结束*/
{
for(i=0;iif[i]!
=0)
{
count++;
if(count==5)/*报数循环*/
{
[i]=0;/*表示队员出列*/
count=0;/*计数器清零*/
s--;/*敢死队员数-1*/
}
}
}
for(i=0;iif[i]!
=0)
if([i]+2)%m==0)/**/
printf("\nThewantedorderis%dth\n",m);
else
printf("\nThewantedorderis%dth\n",[i]+2)%m);
printf("Exitpleaseinput'0'OrGoon...\nPleaseinputthetataloftheteam:
\n");
scanf("%d",&m);/*输入敢死队员总数*/
}
}
三.单循环链表数据结构
(一)、需求分析
1.本程序输入队伍人数n为任意的,最终输出记数的初始位置,首先输入一个报数上限m,当达到报数上限时,那名士兵出列执行任务,从下个人开始记数,再次循环,直到只剩一人,得到其在队伍中的位置,通过数学思想求得题目要求即队长为首的情况下需要记数初始位置。
2.程序执行的命令包括:
(1)构造链表;
(2)数据输入;(3)执行删除;(4)输出要求数值
(5)结束。
3.数据测试:
当n=10,m=5,输出结果为:
要求的位置是:
9
(二)、概要设计
以单循环链表为存储结构,包含三个模块:
1.主程序模块
2.构造链表并初始化
3.删除结点
(三)、详细设计
1.结点类型和指针类型
typedefstructnode
{
intdata;
structnode*next;
}LNode;/*定义结点类型*/
LNode*p;
2.每个模块的分析:
主程序模块:
main()
{
LNode*p;
intm,n,z,y;
do
{
printf("Pleaseinputthepeoplenumber:
\n");
scanf("%d",&n);
}
while(n<=0);
do
{
printf("Pleaseinputtheexcursion:
\n");
scanf("%d",&m);
}
while(m<=0);
if(n=1)
printf("thepositionis:
1");
else
{
p=CREAT(n);
y=DELETE(p,m);
z=n-y+2;
if(z%n==0)/*排除特殊情况*/
printf("thepositionis:
\n%d\n",z);
else
printf("thepositionis:
\n%d\n",(n-y+2)%n);
}/*通过数学思想求得实验要求情况下的数值*/
}
2.构造单循环链表并初始化模块:
LNode*CREAT(intn)/*创建循环链表*/
{
LNode*s,*q,*t;
inti;
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;/*给第i个结点赋值i*/
q=q->next;
}
q->next=t;
}/*生成后续结点,并使其data值即为它所在链表(队伍)中的位置*/
returnt;
}
3.删除结点模块:
DELETE(LNode*t,intm)/*链表的删除*/
{
LNode*a;inti;
while(t->next!
=t)
{
for(i=1;it=t->next;
a=t->next;
t->next=a->next;
free(a);/*释放结点*/
t=t->next;
}/*while循环依次删除被点到的士兵*/
printf("\n");
return(t->data);
}
(四).调试分析:
说明:
(1)本程序的运行环境是TC
(2)进入演示程序后显示提示信息:
Exitpleaseinput'0'OrGoon
Pleaseinputthetataloftheteam:
输入队伍总人数
Pleaseinputtheexcursion:
输入间隔人数
结果显示:
Thewantedpositionisth
选择的位置
(3)调试
程序调试过程中遇到如下警告:
发现错误为if(m=1)
后改正为if(m==1)程序运行正确了,运行如下:
显示输出如图:
(3)由程序分析可得:
本程序时间复杂度为O(nm)!
(4)①在设计生成循环单链表时,考虑到程序结果需要士兵的位序,故将每个结点的data值设置为他们在队列中的位置,方便返回。
②在删除单链表时,如果在报数时直接数到出列士兵则不方便链表的删除,可设置ifor(i=1;it=t->next;
a=t->next;
t->next=a->next;
free(a);/*释放结点*/
t=t->next;
③.在程序设计前,如果按原题所设,则需设队长为第一位,再分别测试从第几个开始才能符合条件。
现在改变思想,通过数学思想:
单循环链表本身是一个循环体,可先求出当从第一个出发的话,求出每隔m个出去一个最后是谁未出列,然后再设置它为链头,求出当他为队首时从第几个开始方能使其不出列。
(n-y+2)%n即可实现这功能!
(五)、设计总结
通过这次课程设计我又学到了很多东西,如程序的模块化设计思想,同时也加深了对数据结构这门课程的理解和学会了如何在实际中应用数据结构.
这个方法是用单循环链表来做的,实现的方法是这样的:
首先从第一号开始报数,循环到指定的偏移位置删除结点,直至剩下一个结点。
然后设计输出,令这个位置为队长位置,队首为开始报数的位置,并按此输出即为所求。
这种方法大大的降低了时间复杂度,复杂度为O(mn)。
(六)、附件
typedefstructnode
{
intdata;
structnode*next;
}LNode;/*定义结点类型*/
LNode*CREAT(intn)/*创建循环链表*/
{
LNode*s,*q,*t;
inti;
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;/*给第i个结点赋值i*/
q=q->next;
}
q->next=t;
}/*生成后续结点,并使其data值即为它所在链表(队伍)中的位置*/
returnt;
}
DELETE(LNode*t,intm)/*链表的删除*/
{
LNode*a;inti;
while(t->next!
=t)
{
for(i=1;it=t->next;
a=t->next;
t->next=a->next;
free(a);/*释放结点*/
t=t->next;
}/*while循环依次删除被点到的士兵*/
printf("\n");
return(t->data);
}
main()
{
LNode*p;
intm,n,z,y;
printf("Exitpleaseinput'0'OrGoon...\nPleaseinputthetataloftheteam:
");
scanf("%d",&n);/*输入队员总数*/
while(n!
=0)/*当队员总数等于0时退出*/
{
do
{
printf("Pleaseinputtheexcursion:
");/*输入偏移数*/
scanf("%d",&m);
}
while(m<=0);
if(m==1)
printf("Thewantedpositionis1th.\n");
else
{
p=CREAT(n);
y=DELETE(p,m);
z=n-y+2;
if(z%n==0)/*排除特殊情况*/
printf("Thewantedpositionis%dth:
\n",z);
else
printf("Thewantedpositionis%dth:
\n",(n-y+2)%n);
}/*通过数学思想求得实验要求情况下的数值*/
printf("Exitpleaseinput'0'OrGoon...\nPleaseinputthetataloftheteam:
");
scanf("%d",&n);/*输入敢死队员总数*/
}
}