🚀 震撼!10道DFS&BFS神级题目让你的算法能力飙升300%

前言

深度优先搜索(DFS)和广度优先搜索(BFS)是算法面试中的核心知识点。本文将通过10个经典的LeetCode题目,深入解析这两种搜索算法的应用场景、解题思路和实现技巧。

1. 二叉树的最大深度 - LeetCode 104

题目描述

给定一个二叉树,找出其最大深度。

解法一:递归DFS

javascript 复制代码
// DFS递归解法
function maxDepth(root) {
    if (!root) return 0;
    return 1 + Math.max(maxDepth(root.left), maxDepth(root.right));
}

解法二:迭代BFS

javascript 复制代码
// BFS迭代解法
function maxDepth(root) {
    if (!root) return 0;
    
    let queue = [root];
    let depth = 0;
    
    while (queue.length > 0) {
        const levelSize = queue.length;
        depth++;
        
        for (let i = 0; i < levelSize; i++) {
            const node = queue.shift();
            if (node.left) queue.push(node.left);
            if (node.right) queue.push(node.right);
        }
    }
    
    return depth;
}

2. 二叉树的层序遍历 - LeetCode 102

题目描述

给你二叉树的根节点 root ,返回其节点值的层序遍历。

BFS解法

javascript 复制代码
function levelOrder(root) {
    if (!root) return [];
    
    const result = [];
    const queue = [root];
    
    while (queue.length > 0) {
        const level = [];
        const levelSize = queue.length;
        
        for (let i = 0; i < levelSize; i++) {
            const node = queue.shift();
            level.push(node.val);
            
            if (node.left) queue.push(node.left);
            if (node.right) queue.push(node.right);
        }
        
        result.push(level);
    }
    
    return result;
}

3. 路径总和 - LeetCode 112

题目描述

判断是否存在从根节点到叶子节点的路径,使得路径上所有节点值之和等于目标和。

DFS解法

javascript 复制代码
function hasPathSum(root, targetSum) {
    if (!root) return false;
    
    // 如果是叶子节点,检查是否等于剩余目标值
    if (!root.left && !root.right) {
        return root.val === targetSum;
    }
    
    // 递归检查左右子树
    return hasPathSum(root.left, targetSum - root.val) || 
           hasPathSum(root.right, targetSum - root.val);
}

4. 岛屿数量 - LeetCode 200

题目描述

给你一个由 '1'(陆地)和 '0'(水)组成的二维网格,请你计算网格中岛屿的数量。

DFS解法

javascript 复制代码
function numIslands(grid) {
    if (!grid || grid.length === 0) return 0;
    
    let count = 0;
    const rows = grid.length;
    const cols = grid[0].length;
    
    for (let i = 0; i < rows; i++) {
        for (let j = 0; j < cols; j++) {
            if (grid[i][j] === '1') {
                count++;
                dfs(grid, i, j);
            }
        }
    }
    
    return count;
}


function dfs(grid, i, j) {
    const rows = grid.length;
    const cols = grid[0].length;
    
    // 边界检查和水域检查
    if (i < 0 || i >= rows || j < 0 || j >= cols || grid[i][j] === '0') {
        return;
    }
    
    // 标记已访问
    grid[i][j] = '0';
    
    // 四个方向递归搜索
    dfs(grid, i + 1, j);
    dfs(grid, i - 1, j);
    dfs(grid, i, j + 1);
    dfs(grid, i, j - 1);
}

5. 单词搜索 - LeetCode 79

题目描述

给定一个 m x n 二维字符网格 board 和一个字符串单词 word。

DFS回溯解法

javascript 复制代码
function exist(board, word) {
    if (!board || board.length === 0) return false;
    
    const rows = board.length;
    const cols = board[0].length;
    
    for (let i = 0; i < rows; i++) {
        for (let j = 0; j < cols; j++) {
            if (dfs(board, word, i, j, 0)) {
                return true;
            }
        }
    }
    
    return false;
}


function dfs(board, word, i, j, index) {
    // 边界检查
    if (i < 0 || i >= board.length || 
        j < 0 || j >= board[0].length || 
        board[i][j] !== word[index]) {
        return false;
    }
    
    // 找到完整单词
    if (index === word.length - 1) {
        return true;
    }
    
    // 标记当前字符为已访问
    const temp = board[i][j];
    board[i][j] = '#';
    
    // 四个方向搜索
    const found = dfs(board, word, i + 1, j, index + 1) ||
                  dfs(board, word, i - 1, j, index + 1) ||
                  dfs(board, word, i, j + 1, index + 1) ||
                  dfs(board, word, i, j - 1, index + 1);
    
    // 回溯,恢复原字符
    board[i][j] = temp;
    
    return found;
}

6. 克隆图 - LeetCode 133

题目描述

给你无向连通图中一个节点的引用,请你返回该图的深拷贝。

BFS解法

javascript 复制代码
function cloneGraph(node) {
    if (!node) return null;
    
    const visited = new Map();
    const queue = [node];
    
    // 克隆第一个节点
    const cloneNode = new Node(node.val);
    visited.set(node, cloneNode);
    
    while (queue.length > 0) {
        const current = queue.shift();
        const cloneCurrent = visited.get(current);
        
        // 处理邻居节点
        for (const neighbor of current.neighbors) {
            if (!visited.has(neighbor)) {
                // 克隆邻居节点
                const cloneNeighbor = new Node(neighbor.val);
                visited.set(neighbor, cloneNeighbor);
                queue.push(neighbor);
            }
            
            // 建立连接关系
            cloneCurrent.neighbors.push(visited.get(neighbor));
        }
    }
    
    return cloneNode;
}

7. 被围绕的区域 - LeetCode 130

题目描述

给定一个二维矩阵,将被 'X' 包围的 'O' 替换为 'X'。

DFS解法

javascript 复制代码
function solve(board) {
    if (!board || board.length === 0) return;
    
    const rows = board.length;
    const cols = board[0].length;
    
    // 从边界上的'O'开始DFS标记
    for (let i = 0; i < rows; i++) {
        for (let j = 0; j < cols; j++) {
            // 边界上的'O'
            if ((i === 0 || i === rows - 1 || j === 0 || j === cols - 1) && 
                board[i][j] === 'O') {
                dfs(board, i, j);
            }
        }
    }
    
    // 处理结果
    for (let i = 0; i < rows; i++) {
        for (let j = 0; j < cols; j++) {
            if (board[i][j] === 'O') {
                board[i][j] = 'X'; // 未被标记的'O'变为'X'
            } else if (board[i][j] === 'T') {
                board[i][j] = 'O'; // 被标记的'T'恢复为'O'
            }
        }
    }
}


function dfs(board, i, j) {
    const rows = board.length;
    const cols = board[0].length;
    
    if (i < 0 || i >= rows || j < 0 || j >= cols || board[i][j] !== 'O') {
        return;
    }
    
    board[i][j] = 'T'; // 标记为临时状态
    
    // 四个方向DFS
    dfs(board, i + 1, j);
    dfs(board, i - 1, j);
    dfs(board, i, j + 1);
    dfs(board, i, j - 1);
}

8. 二叉树的右视图 - LeetCode 199

题目描述

给定一个二叉树,想象自己站在它的右侧,按照从顶部到底部的顺序,返回从右侧所能看到的节点值。

BFS解法

javascript 复制代码
function rightSideView(root) {
    if (!root) return [];
    
    const result = [];
    const queue = [root];
    
    while (queue.length > 0) {
        const levelSize = queue.length;
        
        for (let i = 0; i < levelSize; i++) {
            const node = queue.shift();
            
            // 每层最后一个节点就是右视图可见节点
            if (i === levelSize - 1) {
                result.push(node.val);
            }
            
            if (node.left) queue.push(node.left);
            if (node.right) queue.push(node.right);
        }
    }
    
    return result;
}

9. 矩阵中的最长递增路径 - LeetCode 329

题目描述

给定一个 m x n 整数矩阵,返回从任意单元格开始的所有递增路径中,最长路径的长度。

DFS+记忆化解法

javascript 复制代码
function longestIncreasingPath(matrix) {
    if (!matrix || matrix.length === 0) return 0;
    
    const rows = matrix.length;
    const cols = matrix[0].length;
    const memo = Array(rows).fill().map(() => Array(cols).fill(0));
    let maxLength = 0;
    
    for (let i = 0; i < rows; i++) {
        for (let j = 0; j < cols; j++) {
            maxLength = Math.max(maxLength, dfs(matrix, i, j, memo));
        }
    }
    
    return maxLength;
}


function dfs(matrix, i, j, memo) {
    if (memo[i][j] !== 0) return memo[i][j];
    
    const directions = [[-1, 0], [1, 0], [0, -1], [0, 1]];
    memo[i][j] = 1; // 至少包含自己
    
    for (const [di, dj] of directions) {
        const ni = i + di;
        const nj = j + dj;
        
        if (ni >= 0 && ni < matrix.length && 
            nj >= 0 && nj < matrix[0].length && 
            matrix[ni][nj] > matrix[i][j]) {
            memo[i][j] = Math.max(memo[i][j], 1 + dfs(matrix, ni, nj, memo));
        }
    }
    
    return memo[i][j];
}

10. 课程表 - LeetCode 207

题目描述

你这个学期必须选修 numCourses 门课程,记为 0 到 numCourses - 1。

BFS拓扑排序解法

javascript 复制代码
function canFinish(numCourses, prerequisites) {
    // 构建邻接表和入度数组
    const graph = Array(numCourses).fill().map(() => []);
    const indegree = Array(numCourses).fill(0);
    
    for (const [course, pre] of prerequisites) {
        graph[pre].push(course);
        indegree[course]++;
    }
    
    // 将入度为0的节点加入队列
    const queue = [];
    for (let i = 0; i < numCourses; i++) {
        if (indegree[i] === 0) {
            queue.push(i);
        }
    }
    
    let count = 0;
    
    while (queue.length > 0) {
        const current = queue.shift();
        count++;
        
        // 更新相邻节点的入度
        for (const next of graph[current]) {
            indegree[next]--;
            if (indegree[next] === 0) {
                queue.push(next);
            }
        }
    }
    
    return count === numCourses;
}

算法对比与选择指南

算法 时间复杂度 空间复杂度 适用场景
DFS O(V+E) O(V) 树形结构、路径查找、回溯
BFS O(V+E) O(V) 层级遍历、最短路径、图的连通性

关键技巧总结

1. DFS常见模式

javascript 复制代码
// 基本模板
function dfs(node) {
    if (终止条件) return;
    
    // 处理当前节点
    
    // 递归调用
    dfs(node.left);
    dfs(node.right);
}

2. BFS常见模式

javascript 复制代码
// 基本模板
function bfs(start) {
    const queue = [start];
    const visited = new Set();
    
    while (queue.length > 0) {
        const node = queue.shift();
        if (visited.has(node)) continue;
        visited.add(node);
        
        // 处理当前节点
        
        // 添加邻居节点
        for (const neighbor of getNeighbors(node)) {
            queue.push(neighbor);
        }
    }
}

3. 回溯技巧

javascript 复制代码
// 回溯模板
function backtrack(path, choices) {
    if (满足结束条件) {
        result.add(path);
        return;
    }
    
    for (每个选择) {
        // 做选择
        path.add(choice);
        
        // 递归
        backtrack(path, remainingChoices);
        
        // 撤销选择
        path.removeLast();
    }
}

总结

DFS和BFS是解决图论和树相关问题的核心算法:

  • DFS适用于:路径查找、连通性判断、回溯问题、树的遍历
  • BFS适用于:最短路径、层级遍历、拓扑排序、状态空间搜索

掌握这些经典案例有助于在面试中快速识别问题类型并选择合适的解法策略。

相关推荐
ssshooter2 小时前
WebGL 切换 Shader 的策略
前端·webgl
WDyinh2 小时前
积分球领取补位动画实现
前端·javascript
前端开发爱好者2 小时前
v5.0 版本发布!Vue3 生态最强大的 3D 开发框架!
前端·javascript·vue.js
Sosse2 小时前
window.close()失效 + Chrome浏览器调试线上代码
前端·浏览器
干就完事了2 小时前
Edge 浏览器安装selenium
前端·selenium·edge
Jiezcode3 小时前
LeetCode 48. 旋转图像
c++·算法·leetcode·职场和发展
Greedy Alg3 小时前
LeetCode 230. 二叉搜索树中第 K 小的元素
算法·leetcode·职场和发展
rannn_1113 小时前
【LeetCode hot100|Week4】链表
后端·算法·leetcode·链表
种自己的花呀3 小时前
LeetCode 53 最大子数字和(动态规划)
算法·leetcode·动态规划