马踏棋盘算法——递归实现(回溯法、深度优先遍历)

背景:在学习数据结构时,关于图的遍历的一个经典问题,这里采用递归的方式实现该算法,其中包含回溯法和图的深度优先遍历的思想,在参考各种递归实现该算法的基础上,完成了一版带有自己理解的详细注释版代码

开发环境: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
 
 

posted @ 2020-03-11 13:15  Yu_tiann  阅读(917)  评论(0编辑  收藏  举报