【算法】(BFS/DFS)迷宫路径问题(C语言)

1. 问题分析

题目:现有一迷宫如下图所示,蓝色部分为墙壁,白色部分为通路,入口在左上角(1,1)处,出口在右下角(8,8)处,试找出一条路径以通过该迷宫(路径不能重叠)。
迷宫

分析
① 使用二维数组来存储迷宫,墙用 1 表示,路用 0 表示,如下图所示:
01为与题目中的入口坐标 (1,1) 和出口坐标 (8,8) 对应,二维数组第 0 行和第 0 列不存储迷宫,用 1 填充。

② 对于任意一点(x,y),下一步都有前后左右四个可能的方向,即(x+1,y),(x-1,y),(x,y+1),(x,y-1),使用 fx[4] = { -1,1,0,0 }和 fy[4] = { 0,0,-1,1 } 来模拟下一步走向。

③ 搜索方式可以选择广度优先搜索(BFS)深度优先搜索(DFS)。广度优先搜索利用了队列的先进先出性质,找到的路径是一条最短的通路;深度优先搜索利用了递归回溯的思想,所找到的路径不一定是一条最短通路

2. 基于BFS搜索一条路径

使用数组来模拟队列操作实现广度优先搜索,设置队头队尾指示器qh和qe,通过这两个变量的增减来模拟入队和出队操作。

搜索过程如下图所示:
BFS过程
若其中某一步时队列为空(即qh==qe),说明该迷宫没有通路。

2.1 完整代码及注释

# include<stdio.h>
# define N 9

int maze[N][N] = {
    
    {
    
    1,1,1,1,1,1,1,1,1},		//迷宫,1表示墙,0表示路,入口为maze[1][1],出口为maze[8][8]
				 {
    
    1,0,0,0,0,0,0,0,0},		//maze[0][0]至maze[0][8]和maze[0][0]至maze[8][0](即第一行和第一列)不在迷宫范围内,用1填充。
				 {
    
    1,0,1,1,1,1,0,1,0},
				 {
    
    1,0,0,0,1,1,0,1,0},
				 {
    
    1,0,1,0,0,0,0,1,0},
				 {
    
    1,0,1,0,1,1,0,1,0},
				 {
    
    1,0,1,0,0,0,0,1,1},
				 {
    
    1,0,1,0,0,1,0,0,0},
				 {
    
    1,0,1,1,1,1,1,1,0}};
int fx[4] = {
    
     -1,1,0,0 }, fy[4] = {
    
     0,0,-1,1 };		//每个点的上下左右四个方向扩展

struct {
    
    			//存放路径坐标和前一个坐标的位置
	int x, y, pre;
}sq[100];

int Check(int i, int j);			//检查当前坐标是否可行
void Output(int qe);				//输出路径坐标

/*模拟队列操作实现广度优先搜索*/
void Path_Search() {
    
    
	int i, j, k, qh = 0, qe = 1;
	maze[1][1] = -1;				//置-1表示当前坐标已走过
	sq[1].pre = 0;
	sq[1].x = 1;					//保存当前坐标
	sq[1].y = 1;
	while (qh != qe) {
    
    				//出现qh=qe则说明该迷宫走不通,跳出循环
		qh = qh + 1;
		for (k = 0; k <= 3; k++) {
    
    	//搜索当前坐标的四个扩展方向
			i = sq[qh].x + fx[k];
			j = sq[qh].y + fy[k];
			if (Check(i, j) == 1) {
    
    	//检查是否可行
				qe = qe + 1;
				sq[qe].x = i;
				sq[qe].y = j;
				sq[qe].pre = qh;
				maze[i][j] = -1;
				if (sq[qe].x == 8 && sq[qe].y == 8) {
    
    	//走到(8,8)即出口则结束搜索,输出路径并返回
					Output(qe);
					return;
				}
			}
		}
	}
	printf("未找到出路!\n");		//若循环结束未return则说明走不通
}

/*检查当前坐标是否可行*/
int Check(int i, int j) {
    
    
	int flag = 1;
	if (i < 1 || i > 8 || j < 1 || j > 8)		//是否在迷宫内
		flag = 0;
	if (maze[i][j] == 1 || maze[i][j] == -1)	//是否有路,是否走过
		flag = 0;
	return flag;
}

/*输出路径坐标*/
void Output(int qe) {
    
    
	printf("一条通路为:\n");
	int path_x[100], path_y[100], i = 2, j = 0;
	path_x[1] = sq[qe].x;
	path_y[1] = sq[qe].y;
	while (sq[qe].pre != 0) {
    
    					//将sq中保存的路径保存到path_x和path_y中以便正序输出,若直接输出sq则是倒序
		qe = sq[qe].pre;
		path_x[i] = sq[qe].x;
		path_y[i] = sq[qe].y;
		i++;
	}
	for (i = i - 1; i >= 1; i--) {
    
    				//正序输出路径坐标
		printf("(%d,%d) ", path_x[i], path_y[i]);
		j++;
		if (i != 1)
			printf("-> ");
		if (j % 5 == 0)							//每五个一行
			printf("\n");
	}
}

int main() {
    
    
	Path_Search();
	return 0;
}

2.2 运行结果
BFS

3. 基于DFS搜索一条路径

利用递归和回溯的思想,若某一点的四个扩展方向都走不通,则回溯到上一个可行点。
由于每一点的下一步扩展是用数组 fx[4] = { -1,1,0,0 }和 fy[4] = { 0,0,-1,1 } 来存储的,因此在每一点的搜索顺序是上、下、左、右。

搜索过程如下图所示:
DFS过程

3.1 完整代码及注释

# include<stdio.h>
# define N 9

int maze[N][N] = {
    
     {
    
    1,1,1,1,1,1,1,1,1},		//迷宫,1表示墙,0表示路,入口为maze[1][1],出口为maze[8][8]
				 {
    
    1,0,0,0,0,0,0,0,0},		//maze[0][0]至maze[0][8]和maze[0][0]至maze[8][0](即第一行和第一列)不在迷宫范围内,用1填充。
				 {
    
    1,0,1,1,1,1,0,1,0},
				 {
    
    1,0,0,0,1,1,0,1,0},
				 {
    
    1,0,1,0,0,0,0,1,0},
				 {
    
    1,0,1,0,1,1,0,1,0},
				 {
    
    1,0,1,0,0,0,0,1,1},
				 {
    
    1,0,1,0,0,1,0,0,0},
				 {
    
    1,0,1,1,1,1,1,1,0} };
int fx[4] = {
    
     -1,1,0,0 }, fy[4] = {
    
     0,0,-1,1 };		//每个点的上下左右四个方向扩展

int Check(int i, int j, int k);				//检查当前坐标是否可行
void Output();								//输出路径坐标

void Path_Search(int i, int j, int dep) {
    
    
	int k, newi, newj;
	for (k = 0; k <= 3; k++) {
    
    				//搜索当前坐标的四个扩展方向
		if (Check(i, j, k) == 1) {
    
    
			newi = i + fx[k];
			newj = j + fy[k];
			maze[newi][newj] = dep;			//来到新位置后,设置当前值为搜索深度
			if (newi == 8 && newj == 8) {
    
    	//走到(8,8)即出口则结束搜索,输出路径并返回
				Output();
				return;
			}
			else							//否则进行下一层递归
				Path_Search(newi, newj, dep + 1);
			maze[newi][newj] = 0;			//走不通,回溯
		}
	}
}

/*检查当前坐标是否可行*/
int Check(int i, int j, int k){
    
    
	int flag = 1;
	i = i + fx[k];
	j = j + fy[k];
	if (i < 1 || i > 8 || j < 1 || j > 8)	//是否在迷宫内
		flag = 0;
	if (maze[i][j] != 0)					//是否可行
		flag = 0;
	return flag;
}

/*输出路径坐标*/
void Output() {
    
    
	printf("一条通路为:\n");
	for (int i = 1; i <= 8; i++) {
    
    
		for (int j = 1; j <= 8; j++) {
    
    
			printf("%2d ", maze[i][j]);
		}
		printf("\n");
	}
	printf("该走法共 %d 步\n", maze[8][8] - 1);
}


int main() {
    
    
	maze[1][1] = 2;			//为不与用1表示的墙混淆,搜索深度从2开始,入口置2,输出时也从值为2的坐标点开始输出
	Path_Search(1, 1, 3);
	return 0;
}

3.2 运行结果
DFS

4. 基于DFS搜索所有可行路径

对上述DFS代码稍加修改,即在找到一条路径后不停止,继续回溯,回到上一个可行点继续寻找下一条路径,即可实现搜索所有可行路径。

4.1 完整代码及注释

/*迷宫问题_DFS实现全部路径搜索*/
# include<stdio.h>
# define N 9

int maze[N][N] = {
    
     {
    
    1,1,1,1,1,1,1,1,1},		//迷宫,1表示墙,0表示路,入口为maze[1][1],出口为maze[8][8]
				 {
    
    1,0,0,0,0,0,0,0,0},		//maze[0][0]至maze[0][8]和maze[0][0]至maze[8][0](即第一行和第一列)不在迷宫范围内,用1填充。
				 {
    
    1,0,1,1,1,1,0,1,0},
				 {
    
    1,0,0,0,1,1,0,1,0},
				 {
    
    1,0,1,0,0,0,0,1,0},
				 {
    
    1,0,1,0,1,1,0,1,0},
				 {
    
    1,0,1,0,0,0,0,1,1},
				 {
    
    1,0,1,0,0,1,0,0,0},
				 {
    
    1,0,1,1,1,1,1,1,0} };
int fx[4] = {
    
     -1,1,0,0 }, fy[4] = {
    
     0,0,-1,1 };		//每个点的上下左右四个方向扩展

int Check(int i, int j, int k);				//检查当前坐标是否可行
void Output();								//输出路径坐标
int count = 0;								//统计走法种数

void Path_Search(int i, int j, int dep) {
    
    
	int k, newi, newj;
	for (k = 0; k <= 3; k++) {
    
    				//搜索当前坐标的四个扩展方向
		if (Check(i, j, k) == 1) {
    
    
			newi = i + fx[k];
			newj = j + fy[k];
			maze[newi][newj] = dep;			//来到新位置后,设置当前值为搜索深度
			if (newi == 8 && newj == 8) {
    
    	//走到(8,8)即出口则结束搜索,输出路径并返回
				count++;
				Output();
			}
			else							//否则进行下一层递归
				Path_Search(newi, newj, dep + 1);
			maze[newi][newj] = 0;			//走不通,回溯
		}
	}
}

/*检查当前坐标是否可行*/
int Check(int i, int j, int k) {
    
    
	int flag = 1;
	i = i + fx[k];
	j = j + fy[k];
	if (i < 1 || i > 8 || j < 1 || j > 8)	//是否在迷宫内
		flag = 0;
	if (maze[i][j] != 0)					//是否可行
		flag = 0;
	return flag;
}

/*输出路径坐标*/
void Output() {
    
    
	printf("第%d种走法为:\n", count);
	for (int i = 1; i <= 8; i++) {
    
    
		for (int j = 1; j <= 8; j++) {
    
    
			printf("%2d ", maze[i][j]);
		}
		printf("\n");
	}
	printf("该走法共 %d 步\n\n", maze[8][8] - 1);
}


int main() {
    
    
	maze[1][1] = 2;			//为不与用1表示的墙混淆,搜索深度从2开始,入口置2,输出时也从值为2的坐标点开始输出
	Path_Search(1, 1, 3);
	return 0;
}

4.2 运行结果
DFS_all
更多算法内容关注我的《算法》专栏https://blog.csdn.net/weixin_51450101/category_11590752.html?spm=1001.2014.3001.5482

猜你喜欢

转载自blog.csdn.net/weixin_51450101/article/details/124373261
今日推荐