【算法图解】图的遍历:BFS vs DFS
BFS 像水波纹扩散,DFS 像走迷宫。本文用同一张图对比两者的遍历顺序、数据结构(队列/栈)、复杂度与适用场景,并给出“最短路/连通块/路径枚举”三类题的选型口诀。
14 分钟阅读
小明
【算法图解】图的遍历:BFS vs DFS
很多图题的第一步不是写代码,而是做选择:
- 我该用 BFS 还是 DFS?
你如果只会说“BFS 用队列,DFS 用栈”,那还不够。
你要能把它讲成“选型逻辑”:
- 目标是什么?
- 约束是什么?
- 图的结构是什么?
这篇文章用一套对比框架把它讲透。
1. 先用一句话区分
- BFS:按层扩散,先近后远
- DFS:一路向深,走到底再回退
对应的数据结构:
- BFS:队列(FIFO)
- DFS:栈/递归(LIFO)
2. 用同一张图对比遍历顺序
假设图是:
const g = {
A: ['B', 'C'],
B: ['D', 'E'],
C: ['F'],
D: [],
E: [],
F: [],
}
从 A 出发:
- BFS 可能是:A, B, C, D, E, F(按层)
- DFS 可能是:A, B, D, E, C, F(一路到底再回)
注意:邻接表顺序不同,遍历顺序可能不同,但“层 vs 深”的性质不变。
3. 复杂度:二者都是 O(V+E)
- 时间:$O(V+E)$
- 空间:$O(V)$(visited + 队列/栈)
区别在“空间形态”:
- BFS 在宽度大的图上队列可能很大
- DFS 在深度大的图上栈可能很深(递归风险)
4. 三类题的选型口诀
4.1 最短路(无权图)→ BFS
因为 BFS 按层扩散:
- 第一次到达目标时,步数最少
4.2 连通块/可达性 → DFS 或 BFS 都行
例如:
- 岛屿数量
- 是否存在路径
DFS 写起来通常更短。
4.3 枚举路径/组合 → DFS + 回溯
因为你需要:
- 走一条路
- 记录选择
- 走不通撤销选择
这就是回溯。
5. 工程化建议
- 递归 DFS 可能爆栈:图节点很多时用显式栈
- JS 里队列不要用
shift():用指针或双端队列 - visited 要尽早标记:避免重复入队/入栈
6. 面试回答模板
- BFS 按层扩散,用队列,适合无权最短路径。
- DFS 一路向深,用递归/栈,适合连通块与路径枚举(回溯)。
- 二者复杂度都是 $O(V+E)$,差异在遍历策略与空间形态。
总结
选 BFS/DFS 的关键不是习惯,而是任务:
- 要最短步数 → BFS
- 要枚举方案 → DFS 回溯
- 要遍历/可达 → 都行,选更好写/更不爆栈的