当前位置: 首页 > news >正文

算法精讲——树(一):DFS 的奇妙探险之旅

算法精讲——树(一):DFS 的奇妙探险之旅🌳🚀

📅 2025 年 03 月 04 日 | 作者:无限大 | 标签:#算法 #树 #DFS


一、开篇小剧场 🎭

想象你是一位在茂密森林 🌲 里探险的冒险家,每次遇到分叉路口时,你都会选择最深的路径一路走到底,直到发现宝藏 💎 或死胡同才返回——这就是我们今天要讲的深度优先搜索(DFS) 的生动写照!


二、DFS 核心原理 🔍

1. 什么是 DFS(深度优先搜索)?

DFS(Depth-First Search)就像探险家的执着精神:

  • 策略不撞南墙不回头,优先探索最深的节点
  • 实现方式:递归 / 栈(Stack)
  • 时间复杂度:O(n)(每个节点访问一次)
核心要点:DFS 是策略,不同顺序遍历是方法

2. 二叉树 DFS 的三种方式 🤸

在这里插入图片描述

遍历结果速查表
遍历类型顺序口诀关键特征场景应用力扣真题
前序根→左→右 1 2 4 5 3 6👑 根 → 左 → 右根节点是第一个访问的结点快速克隆树结构144.前序遍历
中序左→根→右 4 2 5 1 3 6🤔 左 → 根 → 右根节点在中间分割左右子树二叉搜索树特性94.中序遍历
后序左→右→根 4 5 2 6 3 1🎁 左 → 右 → 根根节点是最后一个访问的结点删除树节点145.后序遍历

遍历流程详解

1. 前序遍历(DLR)

访问顺序:根 → 左 → 右步骤分解

  1. 访问根节点 1
  2. 递归遍历左子树(节点 2 的左子树 → 4
  3. 递归遍历右子树(节点 2 的右子树 → 5
  4. 递归遍历根节点的右子树(节点 36

路径图

1 → 2 → 4 → 5 → 3 → 6

2. 中序遍历(LDR)

访问顺序:左 → 根 → 右步骤分解

  1. 递归遍历左子树(节点 2 的左子树 → 4
  2. 访问根节点 2
  3. 递归遍历右子树(节点 2 的右子树 → 5
  4. 访问根节点 1
  5. 递归遍历右子树(节点 3 的左子树为空 → 访问 3 → 访问 6

路径图

4 → 2 → 5 → 1 → 3 → 6

3. 后序遍历(LRD)

访问顺序:左 → 右 → 根步骤分解

  1. 递归遍历左子树(节点 2 的左子树 → 4
  2. 递归遍历右子树(节点 2 的右子树 → 5
  3. 访问根节点 2
  4. 递归遍历右子树(节点 3 的右子树 → 6 → 访问 3
  5. 访问根节点 1

路径图

4 → 5 → 2 → 6 → 3 → 1

三、解题思路具体分析 🔥

1. DFS 问题识别雷达 🕵️

遇到以下特征时优先考虑 DFS:

  • 需要遍历所有可能路径
  • 问题可分解为子树问题
  • 需要回溯操作(如路径记录)
  • 求极值/存在性问题

2. 四步拆解法 💡

在这里插入图片描述

步骤详解表
步骤操作要点示例问题
1将问题转化为树/图结构路径总和 → 路径遍历
2明确当前节点状态当前路径和+剩余目标值
3设置递归终止基线叶子节点判断
4定义向子节点的状态转移方式选择左/右子树

四、通用解题模板 🛠️

递归版模板

public ReturnType dfs(TreeNode node, 附加参数) {
    // 🚩1. 终止条件
    if (node == null) return baseCase;
    if (满足特定条件) return 结果值;

    // ✨2. 处理当前节点(前序位置)
    处理逻辑;

    // 🌳3. 递归子节点
    ReturnType left = dfs(node.left, 更新参数);
    ReturnType right = dfs(node.right, 更新参数);

    // 🎯4. 后序处理(可选)
    return 合并结果(left, right);
}

迭代版模板

public ReturnType dfsIterative(TreeNode root) {
    Stack<TreeNode> stack = new Stack<>();
    stack.push(root);

    while (!stack.isEmpty()) {
        TreeNode node = stack.pop();
        // 💡注意入栈顺序:前序->右左入栈,中序->特殊处理
        if (节点需处理) {
            处理逻辑;
        }
        // 按遍历顺序反向压栈
        if (node.right != null) stack.push(node.right);
        if (node.left != null) stack.push(node.left);
    }
    return 结果;
}
模板选择指南
场景推荐模板原因
简单路径问题递归代码简洁直观
复杂状态管理迭代避免栈溢出
需要回溯操作递归+全局变量方便状态回退
严格深度优先迭代显式控制栈操作

五、代码实战演练 ⚔️

案例1:路径总和(力扣112)

// ✅递归模板的完美实践
public boolean hasPathSum(TreeNode root, int targetSum) {
    // 🚩终止条件1:空节点
    if (root == null) return false;

    // 🚩终止条件2:叶子节点
    if (root.left == null && root.right == null) {
        return targetSum == root.val; // ✨结果判断
    }

    // 🌳递归子节点(更新剩余目标值)
    return hasPathSum(root.left, targetSum - root.val)
        || hasPathSum(root.right, targetSum - root.val);
}

案例2:二叉树的中序遍历(力扣94)

// ✅迭代模板的典型应用
public List<Integer> inorderTraversal(TreeNode root) {
    List<Integer> res = new ArrayList<>();
    Stack<TreeNode> stack = new Stack<>();
    TreeNode curr = root;

    // 🎮显式控制遍历流程
    while (curr != null || !stack.isEmpty()) {
        // 🌳左探针直达最深处
        while (curr != null) {
            stack.push(curr);
            curr = curr.left;
        }
        curr = stack.pop();
        res.add(curr.val); // ✨访问节点
        curr = curr.right; // ➡️转向右子树
    }
    return res;
}
图例分析

在这里插入图片描述


六、避坑指南 ⚠️

常见错误对照表

错误现象错误原因解决方案
栈溢出(StackOverflow)递归深度超过系统限制改用迭代+手动维护栈
路径结果重复未及时回溯状态添加 path.removeLast()
空指针异常未判断节点是否为null添加 if(node==null)检查
死循环图中未标记已访问节点使用 visited集合记录

七、知识宇宙扩展 🪐

DFS 变种应用表

变种类型应用场景经典题目
记忆化 DFS重叠子问题优化70.爬楼梯
双向 DFS超大搜索空间优化127.单词接龙
剪枝 DFS组合类问题优化39.组合总和

在这里插入图片描述


八、今日小结 📌

  • 🧭 DFS 是纵向搜索的典型代表
  • 🛠 掌握递归与迭代两种实现方式
  • 🎮 通过树类问题理解 DFS 的精髓

九、下期剧透 🔮

明日主题 :BFS 层序遍历的魔法——像水波纹一样扫描整棵树!

亮点预告

  • 🌀 队列(Queue)的妙用技巧
  • 🎯 最短路径问题的破解之道
  • 💡 双向 BFS 优化秘籍

🌟 课后作业 :用 DFS 实现二叉树的镜像翻转,把你的代码截图发到评论区吧!

相关文章:

  • Linux 运维安全加固策略:实战指南
  • Docker Desktop 4.38 安装与配置全流程指南(Windows平台)
  • π0源码解析——一个模型控制7种机械臂:对开源VLA sota之π0源码的全面分析,含我司的部分落地实践
  • LabVIEW中实现FFT并提取幅值与相位
  • 立即释放 Mac 空间!Duplicate File Finder 8 重复文件高速清理工具
  • Docker Desktop常见问题记录
  • CentOS7安装MySQL5.7到指定数据目录
  • 低代码开发平台(Low-Code)简要介绍
  • 客户端的ip和端口的发送,存储位置和服务端的ip和端口的绑定
  • MySQL知识点总结(二十)
  • 【Python项目】基于深度学习的车辆特征分析系统
  • pytest结合allure
  • 中级系统运维工程师
  • 【江科大STM32】TIM输入捕获模式PWMI模式测频率
  • css梯形tab
  • Java学习笔记-文件命名
  • 华为:Wireshark的OSPF抓包分析过程
  • 如何使用SSH命令安全连接并转发端口到远程服务器
  • K8S学习之基础八:k8s中pod的状态和重启策略
  • 数据库索引的作用:提升数据检索效率的关键
  • html5基础知识/seo搜索优化工程师招聘
  • 昆明做网站外包/域名解析
  • 工商网站查询个人信息/seo优化公司信
  • wordpress博客漏洞/搜索引擎优化的主题
  • 搜索引擎不收录网站/360优化大师官网
  • 广州微网站开发/百度 营销中心