原始题目
下面 BFS 代码的横线处应该填什么?
void bfs(int start) {
queue<int> q;
q.push(start);
dist[start] = 0;
while (!q.empty()) {
int u = q.front(); q.pop();
for (int v : g[u]) {
if (dist[v] == -1) {
dist[v] = ___;
q.push(v);
}
}
}
}
- A.
dist[u] - B.
dist[u] + 1 - C.
dist[v] + 1 - D.
1
答案:B(
dist[u] + 1)
BFS 的分层思想(图解)
一句话概括:u 在第 d 层,它的未访问邻居 v 必然在第 d+1 层,所以 dist[v] = dist[u] + 1。
逐项排除
| 选项 | 判断 | 错在哪 |
|---|---|---|
A. dist[u] |
❌ | 少加 1,所有邻居的距离都和 u 相同,层次被压平 |
B. dist[u] + 1 |
✅ | 从 u 再走一步到 v |
C. dist[v] + 1 |
❌ | 此刻 dist[v] 还是 -1(这正是进入 if 的条件),自己加自己逻辑不通 |
D. 1 |
❌ | 只有 start 的直接邻居才是 1,第二层开始全错 |
队列过程模拟
| 出队 u | dist[u] | 新发现 v | 写入 dist[v] |
|---|---|---|---|
| S | 0 | A, B, C | 1, 1, 1 |
| A | 1 | D, E | 2, 2 |
| B | 1 | F | 2 |
| C | 1 | G | 2 |
| E | 2 | H | 3 |
| G | 2 | I | 3 |
注意 E 在出队时发现 H —— 这正说明扩展是按层推进的,任意时刻队列里的节点层数最多相差 1。
为什么 BFS「一发现就是最短」
- 队列按层推进:处理完所有距离为 d 的节点,才会开始处理 d+1 的节点。
dist[v] == -1保证只入队一次:第一次被到达时的层数,就是最少边数。- 所以在无权图(或所有边权都为 1)上,BFS 等价于 Dijkstra——这是初赛最爱考的定性结论。
复杂度
\[O(n + m)\]
每个点入队一次、每条边被检查一次(无向图检查两次,仍是 \(O(n+m)\))。
三个易混延伸
- DFS 不能求最短路:DFS 一条道走到黑,第一次到达不一定是最小层数,除非穷举所有路径。
- 边权不全为 1 时 BFS 失效:要走 Dijkstra(或 01-BFS、SPFA)。
dist初始化成 -1 而不是 0:0 会被当成“距离为 0”的已访问节点,导致漏判;用-1表示未访问。
考点总结(CSP-J/S 初赛、程序填空)
- 程序填空见到
dist[v] == -1→ 紧跟dist[v] = dist[u] + 1,再q.push(v)。 - 顺序别写反:先赋值再入队(先入队可能导致重复入队)。
- BFS = 无权图最短路;DFS = 连通性 / 拓扑 / 回溯。
- 队列单调性:任意时刻队列内节点层数差 ≤ 1。
思考题
如果图的边权有 1 也有 2(不全相等),BFS 还能求最短路吗?应该换成什么?
答案
不能。BFS 的“层数 = 距离”前提被破坏(走 1 条权 2 的边,比走 2 条权 1 的边更近或相等)。应该换成 Dijkstra(正权)。若边权只有 0 和 1,则可用 01-BFS(双端队列,权 0 放队首、权 1 放队尾)。



