数据结构课程设计《马的遍历问题》.docx
《数据结构课程设计《马的遍历问题》.docx》由会员分享,可在线阅读,更多相关《数据结构课程设计《马的遍历问题》.docx(18页珍藏版)》请在冰豆网上搜索。
数据结构课程设计《马的遍历问题》
师学院
《数据结构》课程设计
题目:
马的遍历问题
系别:
专业:
班级:
学生:
学号:
指导
完成日期:
2015.1.6
二、设计思路..................................................................................................................................3
一.问题描述
根据中国象棋棋盘,对任一位置上放置的一个马,均能选择一个合适的路线,使得该棋子能按象棋的规则不重复地走过棋盘上的每一位置。
要求:
(1)依次输出所走过的各位置的坐标。
(2)最好能画出棋盘的图形形式,并在其上动态地标注行走过程。
(3)程序能方便地地移植到其它规格的棋盘上。
二.设计思路
首先,中国象棋是10*9的棋盘,马的走法是“马走日”,忽略“蹩脚马”的情况。
其次,这个题目采用的是算法当中的深度优先算法和回溯法:
在“走到”一个位置后要寻找下一个位置,如果发生“阻塞”的情况,就是后面走不通的情况,则向后回溯,重新寻找。
在寻找下一步的时候,对周围的这几个点进行比较,从而分出优劣程度,即看它们周围可以走的点谁最少,然后就走那条可走路线最少的那条。
经过这样的筛选后,就会为后面的路径寻找提供方便,从而减少回溯次数。
最后,本程序的棋盘和数组类似,因而采用数组进行存储,同时因为有回溯,所以采用栈的方法,并且为了最后打印方便,采用的是顺序栈的方法。
同时还有八个方向的数组,和为栈设计的每个点周围的八个方向那些可以走的数组。
三.算法分析
1、计算一个点周围有几个点函数intCount(intx,inty)
该函数实现的功能是在遍历的过程当中计算一个点周围有几个方向可以走,从而为后面的筛选提供依据。
intCount(intx,inty)
{//计算每个节点周围有几个方向可以走
intcount=0,i=0;
for(i=0;i<8;i++)
if(chessboard[x+1+dir[i].x][y+1+dir[i].y]==0)
count++;
returncount;
}
2、寻找下一个方向函数intFind_Direction(intx,inty)
该函数的功能是在走过一个点之后,寻找下一个适合的点,如果找到返回正常的方向值,否则返回-1。
intFind_Direction(intx,inty)
{//寻找下一个方向
intdire,min=9,count,d=9;
for(dire=0;dire<8;dire++)
{
if(chessboard[x+1+dir[dire].x][y+1+dir[dire].y]==0&&
CanPass[x+1][y+1][dire]==0)
{
count=Count(x+dir[dire].x,y+dir[dire].y);
if(min>count)
{
min=count;
d=dire;
}
}
}
if(d<9)
returnd;
else
return-1;
}
3、栈的相关函数
初始化栈:
voidInit_Path(path*p);p是用到得栈;
判断栈是否是空:
intEmpty(pathp);p是栈,是空的话返回1,否则返回0,时间复杂度为;
压栈函数:
intPush_Path(path*p,pathnodet,intv)p是栈,t是压进去的节点,v是棋盘,时间复杂度为;
出栈函数:
intPop_Path(path*p,pathnode*t)p是栈,t是拿出来的节点,时间复杂度为。
voidInit_Path(path*p)
{//初始化栈
p->top=-1;
}
intPush_Path(path*p,pathnodet,intv)
{//压节点与其向下一位移动的方向入栈
if(p->top>=63+26*v)
return-1;
else
{
p->top++;
p->pa[p->top].x=t.x;
p->pa[p->top].y=t.y;
p->pa[p->top].di=t.di;
return1;
}
}
intEmpty(pathp)
{//判断栈是否为空
if(p.top<0)return1;
return0;
}
4、马的遍历函数:
voidHorse(intx,inty)
这是该算法的精华部分,x,y表示入口地点,v表示棋盘类型即中国象棋,这个函数主体是一个循环,循环里面始终是在找下一个点,如果找到就将该点进栈,找不到则退栈。
直到发生栈为空时退栈或循环结束,前一种情况时会提示找不到路径(虽然不会发生,但是为逻辑严密性依然要如此),后一种情况则打印出走过的正确路径和走过之后的数组。
voidHorse(intx,inty,intv)//x,y表示出发位置
{//马的遍历函数
intnum=1,t,i;
pathp;
pathnodef;
Init_Path(&p);
for(num=1;num<=64+26*v;num++)
{
t=Find_Direction(x,y);
if(t!
=-1)
{//正常找到下一个方向的情况下
chessboard[x+1][y+1]=num;
f.x=x;f.y=y;f.di=t;
Push_Path(&p,f,v);
x=x+dir[t].x;y=y+dir[t].y;
}
elseif(num==64+26*v&&chessboard[x+1][y+1]==0)
{//最后一次时t肯定是-1
chessboard[x+1][y+1]=num;
f.x=x;f.y=y;f.di=t;
Push_Path(&p,f,v);
}
else
{
if(Pop_Path(&p,&f)==-1)
{//出栈且栈为空的情况
printf("!
!
!
!
!
!
!
!
!
!
!
!
无法为您找到一条适合的路径!
!
!
!
!
!
!
!
!
!
!
!
\n");
exit(0);
}
num-=2;
x=f.x;y=f.y;
CanPass[x+1][y+1][f.di]=1;
}//endelse
}
//根据栈XX息打印出马行走路径
printf("马的遍历路径如下:
\n");
for(i=0;i<64+26*v;i++)
{
printf("(%2d,%d)->",p.pa[i].x,p.pa[i].y);
if((i+1)%(8+v)==0)
printf("\b\b\n->");
}
printf("\b\b\n");
printf("根据数组打印结果是:
\n");
for(i=0;i<8+2*v;i++)
{//根据棋盘数组来打印
for(t=0;t<8+v;t++)
printf("%4d",chessboard[i+2][t+2]);
printf("\n");
}
}
5、主函数:
intmain()
提示输入起点位置,这里的起点位置就是日常生活观念中的顺序,开始点是(1,1),而不是数组中的初始位置(0,0),输入错误则提示重新输入,时间复杂度为。
intmain()
{//主函数
intx,y;
charch='y’;
while(ch=='y')
{
printf("中国象棋马的遍历\n:
");
Mark_Che();
Mark_Dir();
while
(1)
{
printf("请输入入口点横坐标(在案1-10之间):
");
scanf("%d",&x);
if(y<1||y>9)
printf("输入错误,请重新输入!
(横坐标在1-10之间)\n");
else
break;
}
while
(1)
{
printf("请输入入口点纵坐标(在1-9之间):
");
scanf("%d",&y);
if(y<1||y>9)
printf("输入错误,请重新输入!
(纵坐标在1-9之间)\n");
else
break;
}
Knight(x,y);
Getchar();
Printf("\n");
printf("是否继续马的遍历(是:
y;否:
其他):
");
fflush(stdin);
scanf("%c",&ch);
}
}
6、棋盘初始化函数voidMark_Che(intv)
此函数作为棋盘初始化函数,因为每次执行程序时,棋盘上必须是全部都没有走过的。
它会自动进行初始化棋盘,在14*13的棋盘上初始化。
初始化后,棋盘大小的区域全部是0,而周围的两堵墙标志为1,时间复杂度为。
voidMark_Che(intv)
{
inti,j;
for(i=0;i<12+2*v;i++)//首先全部标记为0
for(j=0;j<12+v;j++)
chessboard[i][j]=0;
for(i=0;i<2;i++)//前面两行标记为1
for(j=0;j<12+v;j++)
chessboard[i][j]=1;
for(j=0;j<2;j++)//前面两列
for(i=0;i<12+2*v;i++)
chessboard[i][j]=1;
for(j=10+v;j<12+v;j++)//后面两列
for(i=0;i<12+2*v;i++)
chessboard[i][j]=1;
for(i=10+2*v;i<12+2*v;i++)
for(j=0;j<12+v;j++)//后面两行
chessboard[i][j]=1;
}
7、标记初始化函数voidMark_Dir()
此函数和上面的函数功能类似,也是初始化才用,它是为栈的实现提供帮助的。
开始时全部标记为0,表示周围的八个方向都可以走,时间复杂度为。
voidMark_Dir(intv)
{//由于三维数组赋初值比较困难,因而采用单独的函数实现
inti,j,k;
for(i=0;i<12+2*v;i++)
for(j=0;j<12+v;j++)
for(k=0;k<8;k++)
CanPass[i][j][k]=0;
}
四.测试结果
五.源程序
#include
#include
usingnamespacestd;
intchessboard[14][13];//棋盘
intCanPass[14][13][8];//每个棋子的八个方向哪些可以走
typedefstruct
{//棋盘的八个方向
inty,x;
}direction;
directiondir[8]={{2,1},{1,2},{-1,2},{-2,1},{-2,-1},{-1,-2},{1,-2},{2,-1}};//八个方向
//栈的设计
typedefstruct{
intx,y;//走过位置
intdi;//方向
}pathnode;
typedefstruct{
pathnodepa[90];
inttop;
}path;//顺序栈
voidInit_Path(path*p)
{//初始化栈
p->top=-1;
}
intPush_Path(path*p,pathnodet,intv)
{//压节点与其向下一位移动的方向入栈
if(p->top>=63+26*v)
return-1;
else
{
p->top++;
p->pa[p->top].x=t.x;
p->pa[p->top].y=t.y;
p->pa[p->top].di=t.di;
return1;
}
}
intEmpty(pathp)
{//判断栈是否为空
if(p.top<0)return1;
return0;
}
intPop_Path(path*p,pathnode*t)
{//出栈
if(Empty(*p))
return-1;
else
{
t->x=p->pa[p->top].x;
t->y=p->pa[p->top].y;
t->di=p->pa[p->top--].di;
return1;
}
}
intCount(intx,inty)
{//计算每个节点周围有几个方向可以走
intcount=0,i=0;
for(i=0;i<8;i++)
if(chessboard[x+1+dir[i].x][y+1+dir[i].y]==0)
count++;
returncount;
}
intFind_Direction(intx,inty)
{//寻找下一个方向
intdire,min=9,count,d=9;
for(dire=0;dire<8;dire++)
{
if(chessboard[x+1+dir[dire].x][y+1+dir[dire].y]==0&&
CanPass[x+1][y+1][dire]==0)
{
count=Count(x+dir[dire].x,y+dir[dire].y);
if(min>count)
{
min=count;
d=dire;
}
}
}
if(d<9)
returnd;
else
return-1;
}
voidHorse(intx,inty,intv)//x,y表示出发位置
{//马的遍历函数
intnum=1,t,i;
pathp;
pathnodef;
Init_Path(&p);
for(num=1;num<=64+26*v;num++)
{
t=Find_Direction(x,y);
if(t!
=-1)
{//正常找到下一个方向的情况下
chessboard[x+1][y+1]=num;
f.x=x;f.y=y;f.di=t;
Push_Path(&p,f,v);
x=x+dir[t].x;y=y+dir[t].y;
}
elseif(num==64+26*v&&chessboard[x+1][y+1]==0)
{//最后一次时t肯定是-1
chessboard[x+1][y+1]=num;
f.x=x;f.y=y;f.di=t;
Push_Path(&p,f,v);
}
else
{
if(Pop_Path(&p,&f)==-1)
{//出栈且栈为空的情况
printf("!
!
!
!
!
!
!
!
!
!
!
!
无法为您找到一条适合的路径!
!
!
!
!
!
!
!
!
!
!
!
\n");
exit(0);
}
num-=2;
x=f.x;y=f.y;
CanPass[x+1][y+1][f.di]=1;
}//endelse
}
//根据栈XX息打印出马行走路径
printf("马的遍历路径如下:
\n");
for(i=0;i<64+26*v;i++)
{
printf("(%2d,%d)->",p.pa[i].x,p.pa[i].y);
if((i+1)%(8+v)==0)
printf("\b\b\n->");
}
printf("\b\b\n");
printf("根据数组打印结果是:
\n");
for(i=0;i<8+2*v;i++)
{//根据棋盘数组来打印
for(t=0;t<8+v;t++)
printf("%4d",chessboard[i+2][t+2]);
printf("\n");
}
}
voidMark_Dir(intv)
{//由于三维数组赋初值比较困难,因而采用单独的函数实现
inti,j,k;
for(i=0;i<12+2*v;i++)
for(j=0;j<12+v;j++)
for(k=0;k<8;k++)
CanPass[i][j][k]=0;
}
voidMark_Che(intv)
{//标志棋盘函数
inti,j;
for(i=0;i<12+2*v;i++)//首先全部标记为0
for(j=0;j<12+v;j++)
chessboard[i][j]=0;
for(i=0;i<2;i++)//前面两行标记为1
for(j=0;j<12+v;j++)
chessboard[i][j]=1;
for(j=0;j<2;j++)//前面两列
for(i=0;i<12+2*v;i++)
chessboard[i][j]=1;
for(j=10+v;j<12+v;j++)//后面两列
for(i=0;i<12+2*v;i++)
chessboard[i][j]=1;
for(i=10+2*v;i<12+2*v;i++)
for(j=0;j<12+v;j++)//后面两行
chessboard[i][j]=1;
}
intmain()
{//主函数
intx,y,v;
charch='y';
while(ch=='y')
{
while
(1)
{
printf("请选择棋盘类型(0:
国际象棋,1:
中国象棋):
");
scanf("%d",&v);
if(v!
=1&&v!
=0)
printf("输入错误,请重新输入!
\n");
elsebreak;
}
Mark_Che(v);
Mark_Dir(v);
while
(1)
{
printf("请输入入口点横坐标:
");
scanf("%d",&x);
if(x<1||x>8+2*v)
printf("输入错误,请重新输入!
(横坐标在1-%d之间)\n",8+2*v);
else
break;
}
while
(1)
{
printf("请输入入口点纵坐标:
");
scanf("%d",&y);
if(y<1||y>8+v)
printf("输入错误,请重新输入!
(纵坐标在1-%d之间)\n",8+v);
else
break;
}
Horse(x,y,v);
printf("继续(是:
y;否:
其他):
");
fflush(stdin);
scanf("%c",&ch);
}
return0;
}
六.课程设计心得
通过这次的课程设计,认真的看教材,网上查资料,分析总结觉得提高了不少自学方法和动手能力,加深和扩展了老师上课讲的容,对顺序栈、深度优先搜索的关键步骤都更加熟练了,而且也学会了去思考它的实际作用,它可以应用的具体问题,它的缺点以与改进方法,它的思想精髓的应用等,还有就是在调试过程中遇到的困惑,解决后明白,遇到问题才有机会提高,一个问题的解决就又近了一步。