面试知识库

BFS 与 DFS 模板:图、树、网格搜索的核心套路#

BFS 和 DFS 经常一起出现,但它们解决问题的侧重点并不完全一样:

  • DFS 更像一路走到底,适合搜索、连通块、递归遍历
  • BFS 更像一层一层扩散,适合最短步数、层序遍历、波纹传播

⚡ 速记版模板#

// 模板用途:BFS 按层扩展,常用于无权图最短步数与层序遍历
Queue<Integer> queue = new LinkedList<>(); // BFS 队列:先进先出,保证按层扩展
queue.offer(start); // 起点入队
visited[start] = true; // 入队即标记已访问,防止重复入队
while (!queue.isEmpty()) { // 队列非空说明还有待扩展节点
    int current = queue.poll(); // 取出当前层节点
    for (int next : graph.get(current)) { // 遍历 current 的所有邻接点
        if (!visited[next]) { // 仅处理未访问节点
            visited[next] = true; // 先标记
            queue.offer(next); // 再入队,等待后续层处理
        }
    }
}
java

🎯 什么时候想到 BFS 或 DFS?#

典型信号:

  • 网格中的岛屿、路径、感染、扩散
  • 树或图的遍历
  • 连通块统计
  • 无权图最短路径
  • 层序遍历

Hot 100 里的典型题目:

💡 DFS 模板#

适合:

  • 搜索所有可能路径
  • 统计连通块
  • 递归遍历树或图

模板一:网格 DFS#

适用:岛屿数量、连通块染色、网格遍历。

模板二:图 DFS#

class Solution {
    private List<List<Integer>> graph; // 邻接表:graph[u] 存放 u 指向的所有邻居
    private boolean[] visited; // 访问标记数组

    private void dfs(int node) {
        visited[node] = true; // 进入节点即标记,防止环导致死递归

        for (int next : graph.get(node)) { // 深度遍历所有邻居
            if (!visited[next]) { // 只递归未访问节点
                dfs(next);
            }
        }
    }
}
java

💡 BFS 模板#

适合:

  • 最短步数
  • 最少层数
  • 一层一层扩散
  • 树的层序遍历

模板三:队列式 BFS#

模板四:网格 BFS#

🧠 BFS 和 DFS 怎么选?#

  • 求最短路径 / 最少步数:优先 BFS
  • 只要遍历 / 搜索所有方案:常用 DFS
  • 树的层序遍历:BFS
  • 回溯搜索路径:DFS
  • 拓扑排序:通常 BFS 或 DFS 都能做

⚠️ 易错点#

  1. 访问标记时机错误

    • BFS 通常在入队时标记
    • 否则可能重复入队
  2. DFS 忘记终止条件

    • 特别是网格问题,越界和已访问判断要放前面
  3. BFS 层数统计错位

    • 要先取当前层 size
    • 再处理这一整层
  4. 图问题忘记建邻接表

    • 特别是课程表这类题,先建图再遍历

🎨 面试时怎么说#

你可以这样说:

如果题目要的是最少步数,我会优先 BFS,因为它天然按层扩展,第一次到达目标就是最短路径;如果题目要的是遍历或搜索所有可能,我会优先 DFS。

📌 一句话总结#

  • BFS:层层扩散,适合最短路和层序问题
  • DFS:深入搜索,适合遍历、连通块和递归问题