数据结构7————递归解决迷宫问题和马踏棋盘

数据结构7————递归解决迷宫问题和马踏棋盘

一.前言

1. 迷宫问题的描述

在高为H,宽为W的地图中,0代表可以走,1代表障碍物,不重复的走到终点。给定地图和终点求下列问题

  • 子问题1 求出最少步数
  • 子问题2 求出最多步数
  • 子问题3 输出所有走法
2. 马踏棋盘问题的描述

将马放在国际象棋8×8棋盘某个方格中,马按走棋规则进行移动,要求每个方格只进入一次,走遍棋盘上全部64个方格。编制程序,求出马的行走路线,并按求出的行走路线将数字1,2,…,64依次填入一个8×8的方阵并输出。

3.递归

程序调用自身的编程技巧称为递归( recursion)

递归的本质是栈(函数在内存中是使用栈式存储的),所以可以用递归解决,就一定可以用栈解决,在下一篇中我使用非递归方法,解决上面两个问题

二. 迷宫问题

1.思路
  • 将右下左上四个方向分别定义为k=0,1,2,3,定义一个book[H][W]标记数组,如果走过标1,未走过标0;
  • 对于每个点,使其从k=0,1,2,3四个方向依次遍历
    • 如果k方向的坐标1.未出界 2.未走过 3.不是障碍物 则标记已走,将该点k方向的新坐标传入函数中,重复2
    • 反之 则跳过k方向,进行k+1方向的试探
    • 如果四个方向都试探完毕,说明这个点是死路,结束这个函数,取消这个点的标记
    • 如果该点是终点,根据需求进行操作,结束这个函数
  • 个人建议,如果对这个过程不太清晰,可以根据源码,代入数据手工运行一遍
2.找出最少步数
  • 当该点是终点时,将这种走法所需要的步数和当前最小步数比较,如果当前步数小,则更新最小步数
#include <stdio.h>
#include <string.h>
#define H 5
#define W 4 
#define Accept_x 3
#define Accept_y 3
int min=999999,book[10][10];
int a[H][W]={{0,0,1,0},
             {0,0,0,0},
             {0,0,1,1},
			 {0,1,0,0},
			 {0,0,0,1}};
			   
void dfs(int x,int y,int step)
{
	int next[4][2]={{0,1},//右 
	                {1,0},//下 
			        {0,-1},//左 
	                {-1,0}};//上
					
	if(x==Accept_x&&y==Accept_y)
	{
		printf("到达 %d\n",step); 
		if(step<min)//如果步数比当前最小值小,更新
			min=step;
			
		return;//返回上一步 
	} 
	int tx,ty,k;
	for(k=0;k<4;k++)//枚举所有走法 
	{
		tx=x+next[k][0];//下一步 
		ty=y+next[k][1];
		
		if(tx<0||ty<0||ty>=W||tx>=H)//判断是否越界 
			continue;
			
		if(a[tx][ty]!=1&&book[tx][ty]==0)//判断下一步是否被走过和是否有障碍物
		{
			book[tx][ty]=1;//标记
			//printf("入(%d,%d)%d %d\n",tx,ty,k,step);
			dfs(tx,ty,step+1);//走下一个节点
			book[tx][ty]=0;//当该节点退出(回溯)时,取消标记
			//printf("出(%d,%d)%d %d\n",tx,ty,k,step); 
		} 
	}
	return;
	 
}
int main(void)
{
	book[0][0]=1;//标记起始点
	dfs(0,0,0);//第一个参数是起始点的x坐标,第二个参数是起始点的y坐标,第三个参数是步数 
	printf("%d\n",min);
}
3.找出最多步数
  • 和前一个程序类似,只是将最小值改为最大值
4.输出所以走法
  • 建立一个栈,当标记已走过时,入栈,取消标记是时,出栈
  • 当到达终点时,输出栈内的元素
  • 代码中没有关于栈的基本运算函数,文章末尾代码中的git有完整版
SeqStack *SS;
int n,m,p=4,q=3,min=9999999,book[10][10];
int a[5][4]={{0,0,1,0},
             {0,0,0,0},
             {0,0,1,1},
			 {0,1,0,0},
			 {0,0,0,1}};
			   
void dfs(int x,int y,int step)
{
	int next[4][2]={{0,1},//右 
	                {1,0},//下 
			        {0,-1},//左 
	                {-1,0}};//上
					
	if(x==3&&y==3)
	{
		int i;
		printf("到达\n"); 
		if(step<min)//如果步数比当前最小值小,更新
			min=step;
		for(i=0;i<SS->top+1;i++){
			printf("过程(%d,%d)%d\n",SS->data[i].x,SS->data[i].y,SS->data[i].direction); 
		}
		return;//返回上一步 
	} 
	int tx,ty,k;
	for(k=0;k<4;k++)//枚举所有走法 
	{
		tx=x+next[k][0];//下一步 
		ty=y+next[k][1];
		
		if(tx<0||ty<0||ty>=4||tx>=5)//判断是否越界 
			continue;
			
		if(a[tx][ty]!=1&&book[tx][ty]==0)//判断下一步是否被走过和是否有障碍物
		{
			Elemtype e;
			e.x=tx;
			e.y=ty;
			e.direction=k;
			book[tx][ty]=1;//标记
			Push(SS,e);
			//printf("入(%d,%d)%d %d\n",e.x,e.y,e.direction,step);
			dfs(tx,ty,step+1);//走下一个节点
			book[tx][ty]=0;//当该节点退出(回溯)时,取消标记
			//printf("出(%d,%d)%d\n",e.x,e.y,e.direction); 
			Pop(SS,&e);
		} 
	}
	return;
	 
}
int main(void)
{
	Elemtype e;
	SS=InitStack();
	e.x=0;
	e.y=0;
	e.direction=0;
	Push(SS,e);
	book[0][0]=1;//标记起始点
	dfs(0,0,0);//第一个参数是起始点的x坐标,第二个参数是起始点的y坐标,第三个参数是步数 
	printf("%d\n",min);
}

三.马踏棋盘

1.思路

和迷宫问题类似

  • 将每个点可以走的8个方向分别定义为0到7,定义一个book[W][H]
    如果走过,标记当前步数,如果没走过,标记0
  • 对于每个点
    • 如果k方向的坐标1.未出界2.未走过则标记已走(当前步数),将该点k方向的新坐标传入函数中,重复2
    • 反之则跳过k方向,进行k+1方向的试探
    • 如果四个方向都试探完毕,说明这个点是死路,结束这个函数,取消这个点的标记
    • 如果此时步数= W *H,输出本次走法,结束这个函数,寻找下一种走法
2.代码
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <string.h>
#define H 8
#define W 8
int book[H][W] ={0};//标记
int t=0;          
void dfs(int x,int y,int step){
	int next[8][2]={{-2,1}, //下一步走法 
				{-1,2}, 
			    {1,2},
	            {2,1}, 
				{2,-1},
				{1,-2}, 
			    {-1,-2},
	            {-2,-1}};
    int i,j;
    
	if(step==W*H){//打印 
		for(i=0;i<H;i++){ 
		
			for(j=0;j<W;j++)
					printf("%4d",book[i][j]);
			 	printf("\n");
			} 
			t++;
			printf("%d\n",t);
			return ;
	}	
	
	int k,tx,ty;
	for(k=0;k<8;k++){
		tx = x+next[k][0];
		ty = y+next[k][1]; 
		
		if(tx<0||ty<0||tx>=H||ty>=W){
			continue;
		}
		if(book[tx][ty]==0){
			book[tx][ty]=step+1;
			//printf("入(%d %d) %d\n",tx,ty,step+1);
			dfs(tx,ty,step+1);
			//printf("出(%d %d)\n",tx,ty);
			
			book[tx][ty]=0;
		}
	}
	return;
}
int main(void)
{

	book[0][0]=1;//标记起始点
	
	int a=clock();
	dfs(0,0,1);//第一个参数是起始点的x坐标,第二个参数是起始点的y坐标,第三个参数是步数 
	int b=clock();
	printf("\n%lf秒\n",(b-a)/1000.0);
}
 
	            

四.源码

test4中

  • 6
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值