层层向外扩展,像水波一样——BFS 能在无权图中找到最短路径。
BFS(Breadth-First Search,广度优先搜索)解决的是这类问题:从一个起点出发,走到终点最少需要几步?
它的策略很直接——先把离起点近的地方全部走到,再去走远一点的。就像往平静的湖面扔一块石头,水波总是一圈一圈地向外扩散,不会跳过近处直接去远处。
BFS 用队列来实现这个"按距离顺序访问"的过程。队列的特点是先进先出,保证了距离近的节点总是先被处理。
下面是 BFS 解迷宫最短路的完整代码。先整体看一遍,后面再逐行解释每一部分的作用。
| 1 | #include <bits/stdc++.h> |
| 2 | using namespace std; |
| 3 | |
| 4 | const int MAXN = 105; |
| 5 | int n, m; |
| 6 | char grid[MAXN][MAXN]; // '.' 可走,'#' 障碍 |
| 7 | int dist[MAXN][MAXN]; // -1 表示未访问 |
| 8 | int dx[] = {0, 0, 1, -1}; // 四个方向:右、左、下、上 |
| 9 | int dy[] = {1, -1, 0, 0}; |
| 10 | |
| 11 | int BFS(int sr, int sc, int er, int ec) |
| 12 | { |
| 13 | memset(dist, -1, sizeof(dist)); |
| 14 | queue<pair<int, int>> q; |
| 15 | dist[sr][sc] = 0; |
| 16 | q.push({sr, sc}); |
| 17 | |
| 18 | while (!q.empty()) |
| 19 | { |
| 20 | auto [r, c] = q.front(); |
| 21 | q.pop(); |
| 22 | |
| 23 | if (r == er && c == ec) return dist[er][ec]; |
| 24 | |
| 25 | for (int i = 0; i < 4; i++) |
| 26 | { |
| 27 | int nr = r + dx[i]; |
| 28 | int nc = c + dy[i]; |
| 29 | if (nr < 0 || nr >= n || nc < 0 || nc >= m) continue; |
| 30 | if (grid[nr][nc] == '#' || dist[nr][nc] != -1) continue; |
| 31 | dist[nr][nc] = dist[r][c] + 1; |
| 32 | q.push({nr, nc}); |
| 33 | } |
| 34 | } |
| 35 | return -1; // 走不到终点 |
| 36 | } |
代码分成三个部分,分别对应 BFS 的三个核心步骤:
dist 设为 -1,表示"还没去过"。然后把起点的距离设为 0 并放进队列。这是 BFS 的出发点。
dist != -1)。剩下的邻居:记录步数为当前步数 +1,放进队列。
下面用一个 5×5 的迷宫来直观地看 BFS 的扩展过程。格子里的数字是 BFS 算出的"从起点 S 到这个格子最少需要几步",颜色越深表示距离越近。
迷宫里有两条路能从 S 走到 E:蓝色虚线是最短路(8步),橙色虚线是另一条更长的路(10步)。BFS 因为按步数从小到大扩展,会自动找到蓝色那条。
用坐标 (行, 列) 表示格子,追踪队列里的内容,看看 BFS 是怎么一步一步扩展的。
颜色说明:紫色 = 当前从队列取出处理,蓝色 = 等待在队列中,绿色 = 已处理完毕,红色 = 被跳过(障碍或已访问)。
迷宫其实是图的一种特殊形式(每个格子是节点,相邻格子之间有边)。对于一般的图,BFS 的代码结构完全相同,只需要把"四个方向的邻居"换成"邻接表里的相邻节点"就行了:
| 1 | vector<int> adj[MAXN]; // adj[u] 存 u 的所有邻居 |
| 2 | int dist[MAXN]; |
| 3 | |
| 4 | void BFS(int start) |
| 5 | { |
| 6 | memset(dist, -1, sizeof(dist)); |
| 7 | queue<int> q; |
| 8 | dist[start] = 0; |
| 9 | q.push(start); |
| 10 | while (!q.empty()) |
| 11 | { |
| 12 | int u = q.front(); q.pop(); |
| 13 | for (int v : adj[u]) // 遍历 u 的每个邻居 v |
| 14 | { |
| 15 | if (dist[v] != -1) continue; // 已访问,跳过 |
| 16 | dist[v] = dist[u] + 1; |
| 17 | q.push(v); |
| 18 | } |
| 19 | } |
| 20 | } |
BFS 和 DFS 都是搜索算法,选哪个取决于你要问的问题:
| 对比项 | BFS(广度优先) | DFS(深度优先) |
|---|---|---|
| 形象比喻 | 水波扩散,一圈一圈向外 | 走迷宫,一条路走到底再回头 |
| 用的数据结构 | 队列(先进先出) | 栈 / 递归调用栈(先进后出) |
| 能找最短路吗 | ✅ 无权图中保证最短路 | ❌ 不保证,可能走了弯路 |
| 内存占用 | 队列可能很大,O(n) | 栈深度,O(路径长度) |
| 适合解决 | 最少步数、最短距离、层次遍历 | 判断连通性、列举所有方案、回溯 |
| 记忆口诀 | 问"最少几步" → 用 BFS | 问"能不能到/所有走法" → 用 DFS |