一、引言:探索迷宫的智能方法
在解决迷宫最短路径问题时,广度优先搜索(BFS)是一种高效而优雅的算法。与深度优先搜索(DFS)不同,BFS采用"由近及远"的搜索策略,逐层探索所有可能的路径,从而保证首次到达终点时的路径就是最短路径。(对深搜没有了解的同学,可以先看下我写的关于深搜的学习文章)
二、问题描述:迷宫寻路
给定一个R行C列的迷宫,迷宫由'.'(空地)和'#'(障碍物)组成。我们需要计算从左上角(入口)到右下角(出口)的最短步数(包括起点和终点)。移动规则:每次只能向上、下、左、右四个方向移动到相邻的空地格子。
输入示例:
5 5 ..### #.... #.#.# #.#.# #.#..
输出示例:
9
三、BFS算法核心思想
BFS采用队列(Queue) 这一数据结构实现"先进先出"的访问策略:
-
从起点开始,将其加入队列
-
取出队首元素,探索其所有相邻位置
-
将未访问的有效位置加入队列
-
重复直到找到终点或队列为空
这种策略确保算法优先访问距离起点更近的位置,因此首次到达终点时的路径必然是最短路径。
四、完整代码实现
#include <iostream>
#include <queue>
#include <cstring>
using namespace std;
int R, C; // 迷宫的行数和列数
char maze[40][40]; // 存储迷宫
bool visited[40][40]; // 标记访问状态
// 方向数组:右(0,1), 下(1,0), 上(0,-1), 左(-1,0)
int dx[] = {0, 1, 0, -1};
int dy[] = {1, 0, -1, 0};
// 位置结构体:存储坐标和步数
struct Position {
int x, y; // 当前坐标
int steps; // 从起点到当前位置的步数
};
int bfs() {
queue<Position> q;
// 起点入队:位置(0,0),步数为1(包括起点)
q.push({0, 0, 1});
visited[0][0] = true; // 标记起点已访问
while (!q.empty()) {
Position current = q.front();
q.pop();
// 到达终点:右下角(R-1, C-1)
if (current.x == R - 1 && current.y == C - 1) {
return current.steps;
}
// 探索四个方向
for (int i = 0; i < 4; i++) {
int nx = current.x + dx[i]; // 新位置的行坐标
int ny = current.y + dy[i]; // 新位置的列坐标
// 检查新位置是否有效
if (nx < 0 || nx >= R || ny < 0 || ny >= C)
continue; // 超出边界
if (maze[nx][ny] == '#')
continue; // 遇到障碍物
if (visited[nx][ny])
continue; // 已访问过
// 有效位置:标记并加入队列
visited[nx][ny] = true;
q.push({nx, ny, current.steps + 1});
}
}
// 如果没有找到路径(题目保证有解,此返回值不会执行)
return -1;
}
int main() {
// 读入迷宫大小
cin >> R >> C;
// 读入迷宫数据
for (int i = 0; i < R; i++) {
for (int j = 0; j < C; j++) {
cin >> maze[i][j];
}
}
// 初始化访问标记数组
memset(visited, false, sizeof(visited));
// 执行BFS并输出结果
cout << bfs() << endl;
return 0;
}
五、关键代码解析
1. 方向数组:简洁的方向控制
int dx[] = {0, 1, 0, -1};
int dy[] = {1, 0, -1, 0};
这两个数组定义了四个基本移动方向:
-
右:(x+0, y+1)
-
下:(x+1, y+0)
-
左:(x+0, y-1)
-
上:(x-1, y+0)
使用方向数组避免了重复的方向判断代码,使程序更简洁。
2. 位置结构体:信息封装
struct Position {
int x, y; // 当前位置坐标
int steps; // 从起点到当前位置的步数
};
结构体封装了位置信息和步数,便于在队列中存储和传递状态。
3. BFS核心逻辑:逐层探索
while (!q.empty()) {
Position current = q.front();
q.pop();
// 检查是否到达终点
// 探索四个方向
for (int i = 0; i < 4; i++) {
// 计算新位置
// 检查新位置有效性(边界、障碍、访问状态)
// 有效位置入队
}
}
这是BFS的核心循环,每次从队列中取出最早加入的位置(距离起点最近),探索其所有相邻位置。
4. 访问标记:避免重复访问
visited[nx][ny] = true;
每个位置在首次访问时被标记,确保每个位置只被访问一次,避免无限循环和不必要的重复计算。
六、BFS与DFS的对比
| 特性 | BFS | DFS |
|---|---|---|
| 数据结构 | 队列(Queue) | 栈(Stack) |
| 搜索策略 | 广度优先,逐层扩展 | 深度优先,沿路径到底 |
| 空间复杂度 | O(最宽层的节点数) | O(最大深度) |
| 最短路径 | 首次到达即是最短路径 | 需要遍历所有路径找最短 |
| 适用场景 | 无权图最短路径问题 | 所有路径遍历,连通性检查 |
在迷宫最短路径问题中,BFS具有明显优势,因为它无需遍历所有路径就能找到最短路径。
七、总结
广度优先搜索是解决迷宫最短路径问题的经典算法,其核心在于:
-
使用队列管理待访问位置
-
逐层探索保证首次到达终点即是最短路径
-
通过访问标记避免重复计算
C++广度优先搜索(BFS)解决迷宫问题
3171

被折叠的 条评论
为什么被折叠?



