1.问题
以上是书本上对迷宫问题的描述,需要对其中的问题进行求解。
2.求解方案
解决迷宫问题的方法通常涉及使用搜索算法来找到从迷宫的入口到出口的路径。以下是常见的迷宫问题求解方案:
- 深度优先搜索 (DFS):
- 深度优先搜索是一种递归或堆栈基础的算法。
- 从起点开始,尽可能深入迷宫,直到找到出口或发现无法前进为止。
- 如果发现无法前进,回溯到上一个交叉路口,尝试其他路径。
- 这种方法简单直观,但不一定能找到最短路径。
- 广度优先搜索 (BFS):
- 广度优先搜索通过逐层遍历迷宫,从起点开始,首先考虑所有从起点可以到达的位置。
- 然后考虑这些位置可以到达的位置,依此类推,直到找到出口为止。
- 使用队列来实现,确保最先找到的路径是最短路径。
- 这种方法可以找到最短路径,但可能需要更多的内存。
- A*算法:
- A*算法结合了启发式搜索和广度优先搜索的特性。
- 使用一个估算函数(启发式函数)来评估每个位置的“代价”,然后选择代价最低的位置进行探索。
- 估算函数通常包括到达终点的预估代价(例如曼哈顿距离)和已经走过的实际代价。
- 这种方法在考虑了启发式函数的情况下可以更有效地找到最优解。
- 递归回溯:
- 递归回溯是一种基于递归的方法。
- 从起点开始,不断地向四个方向探索,直到找到出口或无法前进。
- 如果无法前进,返回上一个交叉路口,继续探索其他路径,直到找到出口或探索所有可能的路径。
- 迭代深化搜索 (IDS):
- IDS 是一种深度优先搜索的变体,但它逐渐增加搜索深度,直到找到解为止。
- 这种方法的好处是可以在不消耗太多内存的情况下找到最短路径。
- 迷宫生成算法:
- 有时,解决迷宫问题的一部分是生成随机迷宫,其中包括起点和终点。
- 迷宫生成算法(如Prim算法或Kruskal算法)可以用于创建迷宫,然后可以使用上述搜索算法来解决它。
选择哪种方法取决于问题的具体要求和性能需求。DFS和BFS通常是最简单的方法,A*算法通常用于找到最优解。递归回溯适用于较小的迷宫,而IDS可以用于更大的迷宫,而不会耗尽内存。根据问题的复杂性和性能需求,可以选择最合适的方法来解决迷宫问题。
3.代码
在这里代码主要是深搜,我们通过链栈的非递归形式,对迷宫问题进行求解。
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
int maxrow;
int maxcol;
// 迷宫地图,0 表示墙,1 表示通路
int maze[maxrow][maxcol] = {
{1, 1, 1, 0, 1},
{0, 0, 1, 0, 1},
{1, 1, 1, 1, 1},
{1, 0, 0, 0, 0},
{1, 1, 1, 1, 1},
{1, 0, 0, 1, 1}
};
// 定义坐标的结构体
typedef struct {
int x, y;
} Coordinate;
// 定义链表节点的结构体
typedef struct Node {
Coordinate data;
struct Node* next;
} Node;
// 定义栈的结构体
typedef struct {
Node* top;
} Stack;
// 初始化栈
void initializeStack(Stack* stack) {
stack->top = NULL;
}
// 入栈
void pushToStack(Stack* stack, Coordinate coordinate) {
Node* newNode = (Node*)malloc(sizeof(Node));
newNode->data = coordinate;
newNode->next = stack->top;
stack->top = newNode;
}
// 出栈
Coordinate popFromStack(Stack* stack) {
if (stack->top == NULL) {
Coordinate invalidCoordinate = {-1, -1}; // 表示无效坐标
return invalidCoordinate;
}
Node* temp = stack->top;
Coordinate coordinate = temp->data;
stack->top = temp->next;
free(temp);
return coordinate;
}
// 检查坐标是否有效
bool isValidCoordinate(Coordinate coordinate) {
return coordinate.x >= 0 && coordinate.x < maxrow &&
coordinate.y >= 0 && coordinate.y < maxcol &&
migong[coordinate.x][coordinate.y] == 1;
}
// 打印路径
void printPath(Stack* stack) {
Node* currentNode = stack->top;
while (currentNode != NULL) {
printf("(%d, %d) ", currentNode->data.x, currentNode->data.y);
currentNode = currentNode->next;
}
printf("\n");
}
bool solveMaze(Coordinate start, Coordinate end, Stack* pathStack) {
if (!isValidCoordinate(start) || !isValidCoordinate(end) ||
maze[start.x][start.y] == 0 || migong[end.x][end.y] == 0) {
return false;
}
Stack backtrackStack; // 用于回溯的栈
initializeStack(&backtrackStack);
pushToStack(pathStack, start); // 将起点入栈
migong[start.x][start.y] = 2; // 标记起点已访问
while (pathStack->top != NULL) {
Coordinate current = pathStack->top->data;
if (current.x == end.x && current.y == end.y) {
printPath(pathStack); // 打印路径
return true;
}
bool foundNext = false;
// 尝试向四个方向移动
for (int i = 0; i < 4; i++) {
Coordinate next;
switch (i) {
case 0: // 向上移动
next.x = current.x - 1;
next.y = current.y;
break;
case 1: // 向右移动
next.x = current.x;
next.y = current.y + 1;
break;
case 2: // 向下移动
next.x = current.x + 1;
next.y = current.y;
break;
case 3: // 向左移动
next.x = current.x;
next.y = current.y - 1;
break;
default:
break;
}
if (isValidCoordinate(next) && maze[next.x][next.y] == 1) {
pushToStack(&backtrackStack, next); // 入栈待回溯的点
pushToStack(pathStack, next); // 入栈路径
migong[next.x][next.y] = 2; // 标记已访问
foundNext = true;
break;
}
}
if (!foundNext) {
// 如果没有找到下一步可走的点,回溯到上一步
popFromStack(pathStack);
maze[current.x][current.y] = 0; // 标记为未访问
popFromStack(&backtrackStack);
if (backtrackStack.top != NULL) {
// 如果回溯栈不为空,将当前位置移动到上一个回溯点
pathStack->top->data = backtrackStack.top->data;
}
}
}
return false;
}
int main() {
scanf("%d %d", &maxrow, &maxcol);
for(int i = 0; i < maxrow; i++){
for(int j = 0;j < maxcol; j++){
scanf("%d",&migong[i][j]);
}
}
Coordinate start = {0, 0};
Coordinate end = {maxrow - 1, maxcol - 1};
Stack pathStack;
initializeStack(&pathStack);
if (solvemigong(start, end, &pathStack)) {
printf("成功解决迷宫!\n");
} else {
printf("未找到路径。\n");
}
return 0;
}
测试用例
1.成功
{1, 1, 1, 0, 1}, {0, 0, 1, 0, 1}, {1, 1, 1, 1, 1}, {1, 0, 0, 0, 0}, {1, 1, 1, 1, 1}, {1, 0, 0, 1, 1}
{1, 1, 1, 0, 1}, {0, 0, 1, 0, 1}, {1, 1, 1, 1, 1}, {0, 0, 0, 0, 0}, {1, 1, 1, 1, 1}, {1, 0, 0, 1, 1}
2.失败![](https://img-blog.csdnimg.cn/b1dd7c0a131e41ed81c54425638bb12a.png)
4.重要代码分析
1.数据类型的定义
// 定义迷宫的大小,这里我们用全局变量,可以随时进行更改,然后改变我们的迷宫地图,
//来实现地图的很多变化,使我们的例子多样化,保证可以多种形式
int maxrow;
int maxcol;
// 迷宫地图,0 表示墙,1 表示通路,这里就是根据书上的问题进行判断
int migong[1005][1005] = {};
// 定义坐标的结构体,因为是地图,所以需要使用二位坐标点,来定位一个位置的具体地方,
//这样对于我们的后面操作会简单
typedef struct {
int x, y;
} Coordinate;
// 定义链表节点的结构体,它的数据域是上面定义的坐标类型,它的指针域就是它的下一个
typedef struct Node {
Coordinate data;
struct Node* next;
} Node;
// 定义栈的结构体,在这里定义的链栈,将找到的路径放到链栈里面
typedef struct {
Node* top;
} Stack;
2.对于栈的操作
// 初始化栈
void initializeStack(Stack* stack) {
stack->top = NULL;
}
// 入栈,这里主要是利用的头插法,这样也比较形象的可以比拟堆栈,先进后出的特点
void pushToStack(Stack* stack, Coordinate coordinate) {
Node* newNode = (Node*)malloc(sizeof(Node));//分配空间
newNode->data = coordinate;//赋值数据域
newNode->next = stack->top;//对于链栈的操作
stack->top = newNode;
}
// 出栈
Coordinate popFromStack(Stack* stack) {
if (stack->top == NULL) {//首先判断是否栈空
Coordinate invalidCoordinate = {-1, -1}; // 表示无效坐标
return invalidCoordinate;
}
Node* temp = stack->top;
Coordinate coordinate = temp->data;
stack->top = temp->next;//将头指针向下移动
free(temp);//释放节点
return coordinate;
}
3.深搜的重要操作
bool solveMaze(Coordinate start, Coordinate end, Stack* pathStack) {
if (!isValidCoordinate(start) || !isValidCoordinate(end) ||
migong[start.x][start.y] == 0 || migong[end.x][end.y] == 0) {
return false;
}
Stack backtrackStack; // 用于回溯的栈
initializeStack(&backtrackStack);
pushToStack(pathStack, start); // 将起点入栈
migong[start.x][start.y] = 2; // 标记起点已访问
while (pathStack->top != NULL) {
Coordinate current = pathStack->top->data;
if (current.x == end.x && current.y == end.y) {
printPath(pathStack); // 打印路径
return true;
}
bool foundNext = false;
// 尝试向四个方向移动
for (int i = 0; i < 4; i++) {
Coordinate next;
switch (i) {
case 0: // 向上移动
next.x = current.x - 1;
next.y = current.y;
break;
case 1: // 向右移动
next.x = current.x;
next.y = current.y + 1;
break;
case 2: // 向下移动
next.x = current.x + 1;
next.y = current.y;
break;
case 3: // 向左移动
next.x = current.x;
next.y = current.y - 1;
break;
default:
break;
}
if (isValidCoordinate(next) && migong[next.x][next.y] == 1) {
pushToStack(&backtrackStack, next); // 入栈待回溯的点
pushToStack(pathStack, next); // 入栈路径
migong[next.x][next.y] = 2; // 标记已访问
foundNext = true;
break;
}
}
if (!foundNext) {
// 如果没有找到下一步可走的点,回溯到上一步
popFromStack(pathStack);
migong[current.x][current.y] = 0; // 标记为未访问
popFromStack(&backtrackStack);
}
}
return false;
}
4.不需要另外一个栈作为回溯的代码
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
// 定义迷宫的大小
#define MAZE_ROWS 5
#define MAZE_COLS 5
// 迷宫地图,0 表示墙,1 表示通路
int maze[MAZE_ROWS][MAZE_COLS] = {
{1, 1, 1, 1, 1},
{0, 0, 1, 0, 1},
{0, 1, 1, 1, 1},
{0, 0, 0, 0, 1},
{0, 1, 1, 1, 1}
};
// 定义坐标的结构体
typedef struct {
int x, y;
} Coordinate;
// 定义链表节点的结构体
typedef struct Node {
Coordinate data;
struct Node* next;
} Node;
// 定义栈的结构体
typedef struct {
Node* top;
} Stack;
// 初始化栈
void initializeStack(Stack* stack) {
stack->top = NULL;
}
// 入栈
void pushToStack(Stack* stack, Coordinate coordinate) {
Node* newNode = (Node*)malloc(sizeof(Node));
newNode->data = coordinate;
newNode->next = stack->top;
stack->top = newNode;
}
// 出栈
Coordinate popFromStack(Stack* stack) {
if (stack->top == NULL) {
Coordinate invalidCoordinate = {-1, -1}; // 表示无效坐标
return invalidCoordinate;
}
Node* temp = stack->top;
Coordinate coordinate = temp->data;
stack->top = temp->next;
free(temp);
return coordinate;
}
//
Coordinate getFromStack(Stack* stack) {
if (stack->top == NULL) {
Coordinate invalidCoordinate = {-1, -1}; // 表示无效坐标
return invalidCoordinate;
}
return stack->top->data;
}
// 检查坐标是否有效
bool isValidCoordinate(Coordinate coordinate) {
return coordinate.x >= 0 && coordinate.x < MAZE_ROWS &&
coordinate.y >= 0 && coordinate.y < MAZE_COLS &&
maze[coordinate.x][coordinate.y] == 1;
}
// 打印路径
void printPath(Stack* stack) {
Node* currentNode = stack->top;
while (currentNode != NULL) {
printf("(%d, %d) ", currentNode->data.x, currentNode->data.y);
currentNode = currentNode->next;
}
printf("\n");
}
// 使用链表作为栈求解迷宫
bool solveMaze() {
Stack stack;
initializeStack(&stack);
Coordinate start = {0, 0};
Coordinate end = {MAZE_ROWS - 1, MAZE_COLS - 1};
pushToStack(&stack, start);
while (stack.top != NULL) {
Coordinate current = stack.top->data;
if (current.x == end.x && current.y == end.y) {
// 找到了终点,打印路径
printf("找到路径:\n");
printPath(&stack);
return true; // 找到一条路径后即退出
}
// 尝试向四个方向移动
Coordinate next;
bool flag = false;
for (int i = 0; i < 4; i++) {
switch (i) {
case 0: // 向上移动
next.x = current.x - 1;
next.y = current.y;
break;
case 1: // 向右移动
next.x = current.x;
next.y = current.y + 1;
break;
case 2: // 向下移动
next.x = current.x + 1;
next.y = current.y;
break;
case 3: // 向左移动
next.x = current.x;
next.y = current.y - 1;
break;
default:
break;
}
if (isValidCoordinate(next)) {
pushToStack(&stack, next);
// 标记已访问
maze[next.x][next.y] = 2;
flag = true;
}
}
if(!flag){
maze[current.x][current.y] = 0; // 标记为未访问
popFromStack(&stack);
}
}
// 没有找到路径
printf("未找到路径。\n");
return false;
}
int main() {
if (solveMaze()) {
printf("成功解决迷宫!\n");
} else {
printf("未找到路径。\n");
}
return 0;
}
4.其他操作
// 检查坐标是否有效
bool isValidCoordinate(Coordinate coordinate) {
//判断是否有效主要是两点,其一就是要判断越界问题,第二个就是要判断我们是否走过
return coordinate.x >= 0 && coordinate.x < MAZE_ROWS &&
coordinate.y >= 0 && coordinate.y < MAZE_COLS &&
maze[coordinate.x][coordinate.y] == 1;
}
// 打印路径,在找到之后,我们需要打印,这个时候只需要循环遍历我们的栈
void printPath(Stack* stack) {
Node* currentNode = stack->top;
while (currentNode != NULL) {
printf("(%d, %d) ", currentNode->data.x, currentNode->data.y);
currentNode = currentNode->next;
}
printf("\n");
}