程序员面试宝典

一站式面试准备平台

返回分类
algorithms中级

BFS与DFS详解

深入理解广度优先搜索和深度优先搜索的原理、在二叉树和图中的应用

2026-04-15
阅读时间: 8分钟

BFS 与 DFS 详解

BFS 和 DFS 是面试中最基础的搜索算法。关键不是背模板,而是理解为什么 BFS 用队列、DFS 用栈,以及各自适合什么场景

面试考察点

面试官通过这道题想考察:
1. 你是否理解 BFS 和 DFS 的本质区别
2. 你是否能解释为什么用队列/栈
3. 你是否理解各自的应用场景
4. 你是否能处理 BFS/DFS 的变形题

一、BFS - 广度优先搜索

1.1 BFS 的本质(小白解释)

面试官追问:"BFS 为什么用队列?"

BFS = 一圈一圈地扩展

想象向湖里扔石头:

        ○○○○○○○
      ○○○  ●  ○○○     ← 涟漪扩散
      ○○○      ○○○
        ○○○○○○○

从中心开始,一圈一圈向外扩散。

怎么实现?
用队列!

队列 = 先进先出

1. 把起点加入队列
2. 取出队首(最先进来的)
3. 把它的邻居加入队列(队尾)
4. 重复...

先加入的邻居会先被访问 → 一圈一圈扩展!

1.2 BFS 在二叉树中的应用

层序遍历 = BFS

         1        ← 第1层
        / \
       2   3      ← 第2层
      / \   \
     4   5   6    ← 第3层

BFS 结果:1 → 2 → 3 → 4 → 5 → 6

关键:在处理当前层时,下一层的节点已经加入队列
所以当队列非空时,处理完当前层的所有节点后,
自然就到了下一层!

二、DFS - 深度优先搜索

2.1 DFS 的本质

面试官追问:"DFS 为什么用栈(或者递归)?"

DFS = 一条路走到黑

想象走迷宫:

1. 从路口 A 出发,随便选一条路
2. 走到路口 B,再随便选一条
3. 走到死路!回退到上一个路口
4. 选另一条路
5. 重复...

这不就是"最深走过的点,先回退"?

栈 = 先进后出

用显式栈实现 DFS:
1. 把起点压栈
2. 弹出栈顶
3. 把它的邻居压栈(弹出顺序决定遍历顺序)
4. 重复...

递归就是隐式的栈!递归调用的返回顺序就是栈的弹出顺序。

2.2 递归实现 DFS

def dfs(node):
    if node is None:
        return

    # 处理当前节点
    visit(node)

    # 递归遍历子树
    dfs(node.left)
    dfs(node.right)

递归调用栈的变化:

dfs(A)
  → dfs(B)        ← 压栈
      → dfs(D)    ← 压栈
          → dfs(None)  ← 弹栈
          处理 D
          → dfs(None)  ← 弹栈
      处理 B
      → dfs(E)    ← 压栈
          → dfs(None)
          处理 E
          → dfs(None)
      处理 B 完成 ← 弹栈
  处理 A 完成 ← 弹栈

三、BFS vs DFS

3.1 核心区别

┌─────────────────────────────────────────────────┐
│                  BFS vs DFS                      │
├─────────────────────────────────────────────────┤
│                                                  │
│  BFS:队列实现,一圈一圈扩展                      │
│  - 找最短路径(无权图)                         │
│  - 层序遍历                                     │
│  - 空间 = O(最宽层的节点数)                   │
│                                                  │
│  DFS:栈实现,一条路走到黑                      │
│  - 拓扑排序                                     │
│  - 连通分量                                     │
│  - 空间 = O(树的深度)                          │
│                                                  │
└─────────────────────────────────────────────────┘

3.2 什么时候用 BFS?什么时候用 DFS?

用 BFS:
1. 最短路径(无权图)
2. 层序遍历
3. 找所有可达节点

用 DFS:
1. 拓扑排序
2. 检测环
3. 路径搜索
4. 回溯类问题(排列组合)

记忆口诀:
"BFS 找最近,DFS 找全部"

四、面试真题讲解

4.1 二叉树的最大深度

考察点:DFS vs BFS

方法1 - DFS:
def maxDepth(root):
    if root is None:
        return 0

    left_depth = maxDepth(root.left)
    right_depth = maxDepth(root.right)

    return max(left_depth, right_depth) + 1

方法2 - BFS:
def maxDepth(root):
    if not root:
        return 0

    depth = 0
    queue = [root]

    while queue:
        depth += 1
        for _ in range(len(queue)):
            node = queue.pop(0)
            if node.left:
                queue.append(node.left)
            if node.right:
                queue.append(node.right)

    return depth

4.2 岛屿数量(LeetCode 200)

考察点:BFS/DFS 解决连通分量

问题:二维网格中,'1' 是陆地,'0' 是水。
      有多少个岛屿(相连的陆地)?

思路:
遍历每个格子,如果是 '1' 且未访问:
用 BFS/DFS 把这片陆地标记为已访问
岛屿数 + 1

为什么对?
因为 BFS/DFS 能遍历一个连通分量!

五、面试总结

核心要点

BFS = 队列 = 一圈一圈扩展 = 最短路径
DFS = 栈/递归 = 一条路走到黑 = 回溯

BFS 空间取决于最宽层
DFS 空间取决于树/图深度

面试能加分的回答

1. 能解释为什么用队列/栈
   "队列先进先出 = BFS 的一圈一圈扩展"

2. 能说清应用场景
   "找最短路径用 BFS,拓扑排序用 DFS"

3. 能对比复杂度
   "BFS 空间看最宽层,DFS 空间看最大深度"

相关题目

相关标签