马踏棋盘算法——递归实现(回溯法、深度优先遍历)
背景:在学习数据结构时,关于图的遍历的一个经典问题,这里采用递归的方式实现该算法,其中包含回溯法和图的深度优先遍历的思想,在参考各种递归实现该算法的基础上,完成了一版带有自己理解的详细注释版代码
开发环境:Code::Blocks
编译器:GCC
语言:C
代码:(详细注释)
/* 递归+回溯+图深度优先遍历 */
#include <stdio.h>
#include <time.h>
#define X 8
#define Y 8
int chess[X][Y];
/* 判断八个方向是否有可以走的点 */
int nextxy_1(int *x,int *y,int count)
{
/* count表示8个方向 */
switch(count)
{
case 0:
/* 该方向没有超出边界 且 之前未走过 */
if(*x+2<X && *y+1<Y && chess[*x+2][*y+1]==0)
{
/* 该方向可走则修改当前位置 */
*x+=2;
*y+=1;
/* 有方向可走 则返回1 */
return 1;
}
break;
case 1:
if(*x+2<X && *y-1>=0 && chess[*x+2][*y-1]==0)
{
*x+=2;
*y-=1;
return 1;
}
break;
case 2:
if(*x+1<X && *y-2>=0 && chess[*x+1][*y-2]==0)
{
*x+=1;
*y-=2;
return 1;
}
break;
case 3:
if(*x-1>=0 && *y-2>=0 && chess[*x-1][*y-2]==0)
{
*x-=1;
*y-=2;
return 1;
}
break;
case 4:
if(*x-2>=0 && *y-1>=0 && chess[*x-2][*y-1]==0)
{
*x-=2;
*y-=1;
return 1;
}
break;
case 5:
if(*x-2>=0 && *y+1<Y && chess[*x-2][*y+1]==0)
{
*x-=2;
*y+=1;
return 1;
}
break;
case 6:
if(*x-1>=0 && *y+2<Y && chess[*x-1][*y+2]==0)
{
*x-=1;
*y+=2;
return 1;
}
break;
case 7:
if(*x+1<X && *y+2<Y && chess[*x+1][*y+2]==0)
{
*x+=1;
*y+=2;
return 1;
}
break;
default:
/* 无方向可走 */
break;
}
/* 8个方向均不可走 则换回0 */
return 0;
}
/* 判断八个方向是否有可以走的点 优于nextxy_1 */
/* 这里参考网络上的一个优化:https://blog.csdn.net/ppalive_/article/details/47728475 */
/* 采用nexty_1或nextxy_2函数在整理算法理解上并不重要 */
/* 区别在于寻找下一个方向的顺序 */
int nextxy_2(int *x,int *y,int count)
{
switch(count)
{
case 0:
if(*x+2<X && *y-1>=0 && chess[*x+2][*y-1]==0)
{
*x+=2;
*y-=1;
return 1;
}
break;
case 1:
if(*x+2<X && *y+1<Y && chess[*x+2][*y+1]==0)
{
*x+=2;
*y+=1;
return 1;
}
break;
case 2:
if(*x+1<X && *y-2>=0 && chess[*x+1][*y-2]==0)
{
*x+=1;
*y-=2;
return 1;
}
break;
case 3:
if(*x+1<X && *y+2<Y && chess[*x+1][*y+2]==0)
{
*x+=1;
*y+=2;
return 1;
}
break;
case 4:
if(*x-2>=0 && *y-1>=0 && chess[*x-2][*y-1]==0)
{
*x-=2;
*y-=1;
return 1;
}
break;
case 5:
if(*x-2>=0 && *y+1<Y && chess[*x-2][*y+1]==0)
{
*x-=2;
*y+=1;
return 1;
}
break;
case 6:
if(*x-1>=0 && *y-2>=0 && chess[*x-1][*y-2]==0)
{
*x-=1;
*y-=2;
return 1;
}
break;
case 7:
if(*x-1>=0 && *y+2<Y && chess[*x-1][*y+2]==0)
{
*x-=1;
*y+=2;
return 1;
}
break;
default:
break;
}
return 0;
}
/* 输出计算出来的走法 */
void print()
{
int i,j;
for(i=0;i<X;++i)
{
for(j=0;j<Y;++j)
{
/* tab分割 */
printf("%2d\t",chess[i][j]);
}
/* enter分割 */
printf("\n");
}
printf("\n");
}
/* 遍历棋盘 */
int TravelChessBoard(int x,int y,int tag)
{
int x1 = x;
int y1 = y;
int flag = 0;
int count = 0;
/* tag 标记足迹 */
chess[x][y] = tag;
/* 棋盘遍历完成 */
if(tag == X*Y)
{
/* 打印遍历结果 */
print();
return 1;
}
/* 走第一个方向 */
flag = nextxy_2(&x1,&y1,count);
/* 走不通则切换方向(8个方向都尝试) */
while(flag == 0 && count <= 7)
{
++count;
flag=nextxy_2(&x1,&y1,count);
}
/* 如果所有方向都走不通则跳到if(flag==0) 清除当前位置的足迹 */
/* 如果找到能走的方向 就进行下一次递归(走下一步) */
while(flag)
{
/* 完成遍历(TravelChessBoard函数返回1) */
/* 这里如果每一次递归都能在8个方向种找到可以走地方向,则递归会一直深入(深度优先) */
/* 递归一直深入则相当于函数TravelChessBoard每次都只运行到这里 */
if(TravelChessBoard(x1,y1,tag+1))
return 1;
/* 一直到递归到某个深度之后,8个方向都没有可行路径,则TravelChessBoard函数返回一次0 */
/* 这种情况下 TravelChessBoard函数将运行到这里 */
/* 也就是说之前走的路径是无法完成遍历的 所以需要退回上一步 选择其他路径尝试(切换下一个方向) */
/* 这里相当于是退回到了上一层的递归 以下操作读取上一层递归中的坐标 将上一层递归中的方向+1(切换方向) */
/* 当前路径无法完成遍历(TravelChessBoard函数返回0) */
x1=x;y1=y;
++count;
/* 尝试下一个方向 */
flag=nextxy_2(&x1,&y1,count);
/* 方向尝试失败 则切换下一个方向进项尝试 */
/* 这里count的条件为<7的原因是:上一次走的方向已经被证明失败 不在选择与上一次同样的方向 */
while(flag==0 && count<7)
{
++count;
flag=nextxy_2(&x1,&y1,count);
}
/* 如果所有方向再次尝试失败 则再次运行到if(flag==0)语句,即将再次退出一层递归,重新选择该层的路径 */
}
/* 当前位置向各个方向都走不通 */
if(flag==0)
{
/* 则情况当前位置的足迹 */
chess[x][y]=0;
}
return 0;
}
int main()
{
int i,j;
clock_t start,finish;
/* 获取算法开始时的系统tick */
start=clock();
/* 初始化棋盘 */
for(i=0;i<X;++i)
{
for(j=0;j<Y;++j)
chess[i][j]=0;
}
/* 起始点(2,0)遍历棋盘 */
if(!TravelChessBoard(2,0,1))
{
/* 若遍历失败 */
printf("failed!\n");
}
/* 获取算法结束时的系统tick */
finish=clock();
/* 计算算法耗时 */
printf("\n time consume:%f second\n\n",(double)(finish-start)/CLOCKS_PER_SEC);
return 0;
}
——cloud over sky
——2020/3/11