广度优先搜索(BFS)算法详解——以走迷宫问题为例
引言:当算法遇见迷宫
想象你置身于一个复杂的迷宫,如何在最短时间内找到出口?这个问题不仅存在于童话故事中,更是计算机科学中经典的路径搜索问题。本文将带你通过走迷宫问题,深入理解广度优先搜索(BFS)这一强大的算法工具。
一、BFS算法原理剖析
1.1 BFS & DFS
1.2 算法核心思想
广度优先搜索采用"层层推进"的策略:
- 使用队列数据结构管理待探索节点
- 在每一个点处,所有可能到达的不重复的下一个节点都会被添加到队列中
简单来说,在遇到岔路时,会进行分身
,两边同时
进行探索。
1.3 算法实现框架
void bfs(起点) {
初始化队列
标记起点已访问
while (队列不为空) {
取出队首节点
if (到达终点) {
返回结果
}
for (所有相邻节点) {
if (节点合法且未访问) {
标记已访问
加入队列
}
}
}
}
1.4 算法特性分析
- 时间复杂度: O ( V + E ) O(V + E) O(V+E)( V V V为顶点数, E E E为边数)
- 空间复杂度: O ( V ) O(V) O(V)
- 优势:保证找到最短路径
- 局限:空间开销较大
二、走迷宫问题建模
2.1 问题描述
给定一个 n ∗ m n*m n∗m的迷宫:
- 0 0 0表示可通行的路径
- 1 1 1表示障碍物
- 从左上角 ( 1 , 1 ) (1,1) (1,1)出发,寻找到达右下角 ( n , m ) (n,m) (n,m)的最短路径
2.2 问题转化
将迷宫建模为图:
- 节点:每个可通行的格子
- 边:相邻可通行格子之间的连接
- 权重:每条边的权重为 1 1 1
三、BFS解法的精妙实现
ACWing 走迷宫
3.1 关键数据结构
- 队列:存储待探索节点
- 方向数组:定义移动方向
- 访问标记:记录已访问节点(这里标记数组和迷宫地图可以进行公用)
#include <iostream>
#include <queue>
using namespace std;
const int MAXN = 107; // 最大地图尺寸
int maze[MAXN][MAXN]; // 迷宫地图
int n, m; // 迷宫行数和列数
// 定义节点结构
struct Node {
int x, y; // 当前坐标
int steps; // 已走步数
Node(int x, int y, int steps) : x(x), y(y), steps(steps) {}
};
// 方向数组:右、左、下、上
int dx[] = {0, 0, 1, -1};
int dy[] = {1, -1, 0, 0};
// BFS求解最短路径
int bfs(int startX, int startY) {
queue<Node> q;
q.push(Node(startX, startY, 0));
maze[startX][startY] = 1; // 标记起点已访问
while (!q.empty()) {
Node current = q.front();
q.pop();
// 到达终点
if (current.x == n && current.y == m) {
return current.steps;
}
// 尝试四个方向
for (int i = 0; i < 4; i++) {
int nx = current.x + dx[i];
int ny = current.y + dy[i];
// 检查新位置是否合法
if (nx < 1 || ny < 1 || nx > n || ny > m || maze[nx][ny]) {
continue;
}
maze[nx][ny] = 1; // 标记已访问
q.push(Node(nx, ny, current.steps + 1));
}
}
return -1; // 如果没有找到路径
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
cin >> n >> m;
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m; j++) {
cin >> maze[i][j];
}
}
cout << bfs(1, 1) << endl;
return 0;
}
3.2 核心优化技巧
- 即时标记:在入队时立即标记已访问,避免重复访问
- 提前终止:找到终点立即返回
- 边界检查:防止数组越界
四、复杂度分析与优化方向
4.1 时间复杂度
- 最坏情况: O ( n ∗ m ) O(n*m) O(n∗m)
4.2 空间复杂度
- O ( n ∗ m ) O(n*m) O(n∗m)(队列最大长度)
4.3 进阶优化思路
- 双向BFS:从起点和终点同时搜索
- A*算法:结合启发式搜索
- 分层BFS:减少空间占用
五、BFS的广泛应用场景
- 最短路径:无权图的最短路径
- 连通性检测:判断图的连通性
- 状态空间搜索:解决八数码等问题
- 社交网络:计算人际关系距离
结语:算法之美的永恒追求
通过走迷宫问题,我们不仅掌握了BFS的精髓,更体会到算法设计中"简单与高效"的完美统一。从古老的迷宫谜题到现代的网络路由,BFS算法始终闪耀着智慧的光芒。当您下次面对复杂问题时,不妨想想这个简单的队列,它或许就是打开问题之门的钥匙。
思考题:如何修改算法使其可以输出具体的最短路径?
(tips:试着维护一个
p
p
p数组,记录每一个走过的节点的父节点试试呢)