一、题目分析
迷宫问题本质上本质上是对数组的一个遍历,向上下左右四个方向进行遍历。当上下左右四个方向都无法行动时,将路径进行回溯,找到另一个可以行动的方向,直到找到出口。
显然,在遍历的过程中会有很多的问题:
1、当向前运动时,对上下左右四个方向进行遍历就会找到走过的路径,所以需要对走过的路径进行一个标记。
2、也会出现数组越界的问题,需要对下标进行控制。
3、最后是路径存储的问题。如何将走过的路径进行保存,并且如果路不通的时候,如何将路径删除,保存新的路径。显然栈是一个很好的方式来保存路径。按照后进先出的原则,可以将路径倒退删除,进行回溯。
4、既然选择了栈来保存数据,那么打印的时候又会遇到新的问题。我们需要打印从开始到结束的路径,然而栈的特性是后进先出,打印时便会将路径倒过来打印。所以需要另一个栈,来将存储路径的数据倒一遍,再将新栈打印。
显然C语言对这个问题的实现是有些麻烦的,我们需要自己写一个栈出来,再解决剩下的问题。
二、代码分析
1、主函数
int N = 0, M = 0;
scanf("%d%d", &N, &M);
int** maze=(int**)malloc(sizeof(int*) * N);//malloc一个二维数组
for (int i = 0; i < N; i++)
{
maze[i] = (int*)malloc(sizeof(int) * M);
}
for (int i = 0; i < N; i++)//数据输入
{
for (int j = 0; j < M; j++)
scanf("%d", &maze[i][j]);
}
malloc一个二维数组maze,用来作为迷宫。0代表路可以走通,1代表无法走通。当然,malloc的空间要在使用完成后free掉,避免内存泄漏。
Stack path;//利用栈保存路径
typedef struct Pos//位置坐标
{
int row;
int col;
}Pos;
建立一个栈path来存储路径,以及一个坐标结构体来表示位置坐标。栈储存的数据类型即为定义的struct Pos。将栈进行初始化,将坐标设为入口坐标{0,0}。在这里将path定义为了全局变量,方便函数的使用。
StackInit(&path);//初始化栈
Pos cur = {
0,0 };//入口位置坐标
if (GetMazePath(maze, N, M, cur))//判断是否能走到出口
{
PrintPath();//打印路径
}
else
{
printf("no path\n");
}
StackDestory(&path);//销毁栈
for (int i = 0; i < N; i++)
free(maze[i]);
free(maze);//释放二维数组
return 0;
GetMazePath函数是用来判断是否能找到路径,将迷宫maze,行N,列M,入口坐标cur传入,返回布尔值。如果返回值为true,将路径进行打印。PrintPath即为路径打印函数。
将用过的栈进行销毁。
2、GetMazePath
当路走不通的时候,要对路径进行回溯,走到一个岔路口,向另一个方向运动。要采用递归的方式进行回溯。
在上图中,当走到(1,0)位置的时候,下方向和右方向都可以前行。
如果先向下运动,当我们走到(3,1)位置时便无法运动了。向下向左走会越界,向右数组值为1,无法运动。当然,上方向我们要进行一个标记,避免向回走的情况。如下图所示:
暂且标记为2,因为只有0才能通,标记为其它数字也无妨。既然在(3,0)无法行动,便要回到其它位置,(2,0)显然无法走通,(1,0)的右边方向是可以的,我们便要回溯到(1,0)这个位置。
并且在运动的时候,代码无法确定此时正在走的路径能能不能到达终点(3,3)处,所以要实时得对路径在栈中进行Push(保存),或者Pop(删除)。
(1)、是否到达终点
StackPush(&path, cur);//保存路径
if (cur.row == N - 1 && cur.col == M - 1)//判断是否到达终端点
return true;
maze[cur.row][cur.col] = 2;//标记走过的路
在每一次运动时,最先要保存一下路径,之后再判断要不要删除路径。
并且要判断是否到达迷宫的终点(数组右下角),如果以及到达终点就没有继续递归的必要了,直接返回true即可。
以及要对走过的路进行一个标记
(2)、判断下一个方向是否通路
Pos next = cur;//下一个位置坐标
next.row -= 1;//向上走
if