问题描述
给定一个迷宫,指明起点和终点,找出从起点出发到终点的有效可行路径,就是迷宫问题。
问题分析
我们可以使用二维数组来模拟迷宫,用‘0’来表示通路,用‘1’来表示障碍。于是我们可以这样定义这样一个迷宫(左上角为入口,右下角为出口):
// 迷宫的定义,0表示通路,1表示障碍
int maze[ROW][COL] = {
{0, 1, 0, 0, 0},
{0, 1, 1, 1, 0},
{0, 0, 0, 0, 0},
{0, 1, 1, 1, 0},
{0, 0, 0, 1, 0}
};
我们可以想想如何才能找到这条出口:
当我们站在(0,0)点的时候,我们只有一个选择那就是往下走,因为右边是障碍物,当我们走到(1,0)点的时候,显然我们也只能继续往下走,因为右边是障碍物,上面是刚刚走过的路。但当我们走到(2,0)点的时候,此时我们可以选择两条路,要么继续下,要么往右走。聪明的你肯定能看出来往右走才是正确的,但计算机并没有你聪明,它并不知道要往哪块走,如果它继续往下走的话,当它走到(4,2)点的时候就会发现无路可走了,那怎么办呢?聪明的你又会知道应该让计算机回到(2,0)点继续往右走。
那计算机怎么实现回到(2,0)点呢?那肯定是让计算机按他原来走过的路一步一步回退。那它怎么会知道它走过哪些路呢?那就在地图上把它走过的路记为2。这样在它回到(2,0)点的时候就可以继续判断:上面走过,下面走过,只能走左边了。
那当它走到终点时,只是知道了他这条迷宫有出路,那出路到底是什么呢?我们就可以把它每走过的路存起来。
那说了这么多,我们到底要如何实现呢?本章我介绍一种链栈的思路。每次判断该路可不可行,如果可行,就将该节点存入栈中,如果遇到死路,就往回走,也就是出栈。这样在走到终点的时候,栈中存放的节点就正好是迷宫的一条出路。
代码实现
我们可以这样定义栈的结点x,y为结点坐标:
typedef struct node {
int x;
int y;
struct node* next;
} LNode, *Node;
使用两个数组来实现上下左右的移动:
int dirRow[] = {0, 1, 0, -1};
int dirCol[] = {1, 0, -1, 0};
//newx和newy为移动后的结点坐标
for (int i = 0; i < 4; i++) {
int newx = (*p)->x + dirRow[i];
int newy = (*p)->y + dirCol[i];
}
我们来看关键部分代码:
//判断是否为可走的路
int IsPass(int x, int y) {
return x >= 0 && x < MAXSIZE && y >= 0 && y < MAXSIZE && Map[x][y] == 0;
}
//寻找通路
int FindPath(Node* p) {
int t=0;
while (1){
if((*p)->x==MAXSIZE-1&&(*p)->y == MAXSIZE-1){
return 1;
}
t=0;
for (int i = 0; i < 4; i++) {
int newx = (*p)->x + dirRow[i];
int newy = (*p)->y + dirCol[i];
if (IsPass(newx, newy)) {
Push(p,newx,newy);//入栈
Map[newx][newy] = 2;
t=1;
break;
}
}
if(t==0){
Pop(p);//出栈
}
}
return 0;
}
其中 int IsPass(int x, int y) 函数用来判断进行移动后的那一个点是否可行:是否超出数组范围,是否有障碍。
在 while 循环中,先判断此结点是否为出口,如果是直接退出函数。随后的 for 循环就是将四个方向尝试走一遍,如果哪条路可走,就将该路入栈,标记已走,然后直接退出for循环。
通过变量 t 判断在for循环中是否入过栈,即是否有路。如果t值为0,那就代表for循环中四个方向都不能走,即死路,那么就往前退一格,即出栈。
完整代码
#include <stdio.h>
#include <stdbool.h>
#include <stdlib.h>
#define ROW 5
#define COL 5
// 迷宫的定义,0表示通路,1表示障碍
int maze[ROW][COL] = {
{0, 1, 0, 0, 0},
{0, 1, 1, 1, 0},
{0, 0, 0, 0, 0},
{0, 1, 1, 1, 0},
{0, 0, 0, 1, 0}
};
// 存储路径的坐标
typedef struct {
int row;
int col;
} Coordinate;
// 定义方向:东、南、西、北
int dirRow[] = {0, 1, 0, -1};
int dirCol[] = {1, 0, -1, 0};
// 函数声明
bool findPath();
int main() {
if (findPath()) {
// 打印路径
printf("找到通路,路径如下:\n");
for (int i = 0; i < ROW; i++) {
for (int j = 0; j < COL; j++) {
if (maze[i][j] == 2) {
printf("-> ");
} else {
printf("%d ", maze[i][j]);
}
}
printf("\n");
}
} else {
printf("没有找到通路。\n");
}
return 0;
}
// 栈的结点定义
typedef struct StackNode {
Coordinate data;
struct StackNode* next;
} StackNode;
// 栈的定义
typedef struct {
StackNode* top;
} Stack;
// 初始化栈
void initStack(Stack* stack) {
stack->top = NULL;
}
// 判断栈是否为空
bool isEmpty(Stack* stack) {
return stack->top == NULL;
}
// 入栈
void push(Stack* stack, Coordinate data) {
StackNode* newNode = (StackNode*)malloc(sizeof(StackNode));
newNode->data = data;
newNode->next = stack->top;
stack->top = newNode;
}
// 出栈
bool pop(Stack* stack) {
if (isEmpty(stack)) {
return false;
}
StackNode* temp = stack->top;
stack->top = stack->top->next;
free(temp);
return true;
}
// 获取栈顶元素
Coordinate top(Stack* stack) {
Coordinate data = {0, 0};
if (!isEmpty(stack)) {
data = stack->top->data;
}
return data;
}
// 判断当前位置是否有效
bool isValidMove(int row, int col) {
return (row >= 0 && row < ROW && col >= 0 && col < COL && maze[row][col] == 0);
}
// 在迷宫中查找通路
bool findPath() {
Stack stack;
initStack(&stack);
Coordinate start = {0, 0};
push(&stack, start);
maze[start.row][start.col] = 2; // 标记入口
while (!isEmpty(&stack)) {
Coordinate current = top(&stack);
if (current.row == ROW - 1 && current.col == COL - 1) {
// 已经到达出口
return true;
}
bool found = false;
for (int i = 0; i < 4; i++) {
int newRow = current.row + dirRow[i];
int newCol = current.col + dirCol[i];
if (isValidMove(newRow, newCol)) {
Coordinate next = {newRow, newCol};
push(&stack, next);
maze[newRow][newCol] = 2; // 标记已访问
found = true;
break;
}
}
if (!found) {
// 无法继续前进,回溯
pop(&stack);
}
}
return false; // 没有找到通路
}