数据结构与算法课程设计报告Word格式.docx
《数据结构与算法课程设计报告Word格式.docx》由会员分享,可在线阅读,更多相关《数据结构与算法课程设计报告Word格式.docx(14页珍藏版)》请在冰豆网上搜索。
4、如何找到一条满足条件的路径。
5、如何确定这条路径是最短的。
本问题的关键和难点是如何根据骑士的走法规则,找出一条最短路径。
h
g
f
e
d
c
b
a|0
1
2
3
4
5
6
7
图1:
8*8的棋盘
如图,建立如上的坐标系,于是每个点的位置就可以用一个有序数对来表示。
如:
(a,3),(b,6),(h,2)。
对于任意一点的马最多有8中走法可以选择。
根据马行走后行列坐标数值大小的增加和减少情况的变化,用有序数组对来表示其走法。
staticintbx[8]={2,1,-1,-2,-2,-1,1,2};
staticintby[8]={1,2,2,1,-1,-2,-2,-1};
其中,bx数组与by数组下标相同的变量表示一种走法。
骑士在棋盘中的位置可由结构体类型
typedefstruct
{
intn,m;
//n记录行数,m记录列数
intch;
}node;
来表示。
对于骑士的起始与终点位置可由有序对(x0,y0),(x1,y1)来表示。
另外建立一个队列,
typedefstruct//定义顺序队列类型
nodedata[maxlen];
intfront;
intrear;
}Sequeue;
把骑士从起点到终点的每一步入队,前一步出对。
二、概要设计与数据结构选择
对于所提出的关键问题,即找出从起点到终点的最短步数,这里所用到的方法是遍历起点与终点的所有路径,记录下其步数,然后比较其中最小的步数,即为最短步数。
1、输入起点和终点的坐标,起点先入对;
2、按照上述的8走法分别走一步,如果到达终点,计步器加一,如果未到终点,计步器加一并入对;
(所有走法均入队)
3、出对,以步骤2中的如对顺序分别把其作为新起点,重复步骤2;
4、判断是否对空,对空则以走过全部路径,对未空,重复步骤3;
对空,转到步骤5;
5、比较每种路径的步数,选出最小的数值,即为最小步数。
以上操作相当于对于一棵8个结点的完全树进行遍历,查找满足条件的结点,并找出这些结点中结点高度最低的结点。
以上过程可由树来表示。
树如图2:
…………
…………………………
………………………………
图2:
骑士游历的树结构
如图3,先找到起点,即根节点,并进行入队操作。
由起点逐次访问起点的各个子树的根节点,比较是否等于终点的坐标,若相等,则保存路径长度;
若不相等,则把不相等的子树的根节点进行入队操作。
访问完所有子树的根节点后,进行一次出队操作;
即把根节点出队。
此后顺次访问队头节点的子树的根节点,即顺次访问根节点的子树的根节点的各个子树的根节点,比较是否等于终点的坐标,若相等,则保存路径长度;
直到队为空为止。
访问顺序如下:
…………
…
图3:
游历的访问顺序
数据结构如下:
typedefstruct//骑士位置结构体
intn,m;
intch;
typedefstruct//顺序队列类型结构体
nodedata[maxlen];
intfront;
intrear;
图4:
详细设计流程图
三、详细设计和编码
首先,对于在问题分析和任务定义中所提出的问题逐个进行解决。
定义棋盘和走法,并对其进行初始化。
intway[8][8]表示棋盘每一点的位置,例如,way[2][2]表示第三行,第三列的坐标,way[3][6]表示第四行,第七列的坐标。
因为在本实验中并没有要求骑士行走的路径,所以棋盘可不显示的定义。
对于走法,我们前面已经提到了,对于马在棋盘中的任意位置,最多有8种走法,用有序数对表示即:
(2,1),(2,-1)(1,2),(1.-2)(-2,1),(-2,-1),(-1,2),(-1,-2)。
故,我们可以做如下定义:
所以在骑士的行走过程中,只要让骑士的位置即坐标加上或减去上述8中走法的数组表示。
对于骑士的起点和终点的位置表示,根据要求位置由两个字符组成,一个是小写字母(a-h),一个是数字(1-8)。
所以有如下的表示方法:
intx0,y0,x1,y1;
charxa,xb;
x0=xa-‘a’;
x1=xb-‘a’;
其中,x0、y0表示起点坐标,x1、y1表示终点坐标,输入的xa,xb为字符,减去‘a’,便可转换成数字。
这样便可以把输入的字符坐标转换成用数字表示的坐标。
在得到骑士的起点终点坐标之后,便可以把起点入队,入队函数如下所示:
voidadd(Sequeue*S,intx,inty,intz)//入队函数
if(S->
rear<
maxlen-1&
&
S->
rear>
=0)
{
S->
rear++;
data[S->
rear].n=x;
rear].m=y;
rear].ch=z;
}
elseprintf("
error\n"
);
}
这里的参数x,y,z分别表示行坐标,列坐标,和到达该坐标从起点所走的步数。
它们的值分别保存在队列的n,m,ch中。
在对起点进行入队之后,便可进行运动了,即骑士可以按规则(8种走法)走向下一位置。
此后,在一个while循环中进行如下的操作,while循环的条件为:
S->
front<
rear,保证在队列不为空的情况下进行循环。
对于骑士的8种走法,可以通过for(i=0;
i<
8;
i++)循环进行逐个试探。
这里可以定义两个证型变量x3、y3;
用以存放骑士走一步后的位置,即坐标。
则有如下定义:
x3=bx[i]+x0;
y3=by[i]+y0;
又由实际情况可知,x3、y3应该满足x3、y3同时大于等于0且小于8;
在此时设置标记数组step[maxlen][maxlen]=0;
标记数组用以标记该位置是否已经走过,走过则置1;
所以x3、y3还应满足step[x3][y3]==0;
即(x3,y3)这个位置还未走过。
若满足以上条件,则可以定义node*p;
并为其申请内存空间。
有如下一段程序:
p=(node*)malloc(sizeof(node));
p->
ch=S->
front+1].ch+1;
n=x3;
m=y3;
step[x3][y3]=1;
把(x3,y3)的值放入指针p指向的空间的(n,m)中,用以保存此位置;
因为ch存放的是从起点到该位置所走的步数,故p->
即它表示的意思是从起点x3、y3所走的步数。
此时并把x3y3的标记置为1,说明此位置已经被访问过了。
这时便可以对x3、y3进行判断了。
若p->
n==x1&
p->
m==y1,即比较x3、y3与终点的坐标是否相同。
若相同则找到一种走法。
此时定义inta=0;
intcount[maxlen];
count[a]数组的作用就是记录下每一种走法的步数;
令count[a]=p->
ch即可。
若不满足条件的话则把x3、y3入队;
然后i++;
重复上述操作,直到i不满足条件为止,即已经走完了8种走法;
此后,进行出栈操作;
判断S->
front与S->
rear的大小关系,若S->
front>
rear,则进行while循环,若不满足,则跳出while循环。
最后,对计数数组count[a]中的值进行比较,找出最小值,即为,从终点到起点的最小步数。
程序如下:
intmin;
min=count[0];
for(i=1;
a;
i++)
if(min>
count[i])
min=count[i];
printf("
最短步数为:
%d\n"
min);
四、上机调试
1、语法错误及修正:
本程序使用了循环思想和队列的数据结构,所以程序出现的语法错误主要在于队列函数的调用及变量的定义,关键字和函数名称的书写,以及一些库函数的规范使用。
这些问题均可以根据编译器的警告提示,对应的将其解决。
2、逻辑问题的修改和调整:
循环思想的使用虽然简化了程序,但是增加了对函数循环控制的难度。
在while循环中,要实现对所有路径的查找,而不能丢掉一条,也许丢掉的便是我们所需要的,这样便造成了错误。
又因为我们对路径的查找使用了队列的数据结构,即对坐标进行了入队和出对的操作。
所以我们可以用对空的条件作为while循环的终止条件。
这样便可以无遗漏的查找起点到终点的所有路径。
还有一点容易出现逻辑错误的是在何时进行入队和出对的操作。
对入队的操作而言,在每一次行走后对于不满足条件的坐标均要进行入队操作。
而对于出对而言,每次出队操作均在对每一步的八种走法走完之后才进行,这样便可以保证路径查找没有遗漏。
3、时间,空间性能分析:
因为本算法需要用一个二维数组保存每一个位置的访问状态,也需要一个一维数组保存访问到的满足条件的步数,故其空间复杂度较高,会占据较大的内存空间,其空间复杂度为O(maxlen*maxlen)。
但是对于本算法而言,空间复杂度不但很高,时间复杂度也很大。
由于本算法将对路径的遍历转变成了对树的遍历,需要遍历所有的路径并保存,从中找出满足条件的路径。
所以无论起点和终点的位置如何,都需要进行所有的查找过程,所以对于本算法而言,时间复杂度很大,为O(8^n*n)。
由此可得,本算法的时间空间性能较差,由于知识的局限性,故本人无法对此算法进行优化。
4、经验和体会:
在刚拿到此问题时,感觉无从下手,但是经过仔细的分析,了解到,对骑士路径的查找,对八子树的树的遍历算法很相像,不过要对每次遍历的结果进行判断,从而找出满足条件的结果。
因此在具体解决问题时采用了树的数据结构思想,再经过完善和修改得出算法,并用程序语言实现。
从这个过程中我了解到对问题从认识到建立模型,之后提出方法,修改方法,最终解决问题的过程。
也使我体会到对于具体问题的解决,只要按照解决问题的过程,就不会出现盲目的情况。
五、用户使用说明
本程序运行时带有提示性语句。
开始时,程序会提示你输入骑士游历的起终点,输入格式为每一行都是一组开始位置和结束位置,位置由两个字符组成,一个是小写字母(a-h),一个是数字(0-7),起始位置结束位置由一个空格隔开。
故输入的坐标横坐标的取值范围在(a-h)之间,纵坐标的取值范围在(0-7)之间,输入起终点位置后,按回车,程序会给出从起点到终点的最短步数的值。
之后,程序会提示你是否继续输入,输入’y’表示继续输入,输入‘n’表示不再进行输入,即退出程序。
这样便可以手动的控制输入的次数。
方便查询。
六、测试结果
图五:
运行结果
七、附录
#include"
stdio.h"
#include"
stdlib.h"
#definemaxlen100
intlength=0;
//记录所走的步数
Sequeue*setqueue()
Sequeue*S;
S=(Sequeue*)malloc(sizeof(Sequeue));
front=0;
rear=0;
returnS;
voiddele(Sequeue*s)//出队函数
if(s->
s->
rear)
s->
front++;
else
printf("
voidjudge()//判断步数
S=setqueue();
intx0,y0,x1,y1,x3,y3,i,j;
intcount[maxlen+100];
inta=0;
intstep[maxlen][maxlen];
charxa,xb;
for(i=0;
maxlen;
data[i].ch=0;
请输入骑士游历的起终点:
\n"
scanf("
%c,%d%c,%d"
&
xa,&
y0,&
xb,&
y1);
x0=xa-'
a'
;
x1=xb-'
for(j=0;
j<
j++)
{
step[i][j]=0;
}
step[x0][y0]=1;
add(S,x0,y0,0);
while(S->
node*p;
x0=S->
front+1].n;
y0=S->
front+1].m;
for(inti=0;
x3=bx[i]+x0;
y3=by[i]+y0;
if(x3>
=0&
x3<
8&
y3>
y3<
step[x3][y3]==0)
{
p=(node*)malloc(sizeof(node));
p->
step[x3][y3]=1;
if(p->
m==y1)
{
step[x1][y1]=0;
count[a]=p->
ch;
a++;
}
else
add(S,p->
n,p->
m,p->
ch);
}
dele(S);
intmin;
min=count[0];
for(i=1;
voidmain()
charnn;
nn='
y'
while(nn=='
)
judge();
是否继续输入?
('
是,'
n'
否)\n"
scanf("
%c"
nn);
system("
pause"
八、参考书目
王昆仑,李红。
数据结构与算法。
北京:
铁道工业出版社,2007年5月第一版