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

二叉树(中序遍历)

嘿,欢迎来到小巫blog!小巫又来啦!看到你对二叉树中序遍历这道题有点困惑,别担心,我会一步步带你搞定它!这道题是树的基础题目,掌握了它,你对树的遍历就会有很深的理解。我相信,只要你跟着我的思路走一遍,自己动手写写代码,很快就能独立解决这类问题!咱们现在就来聊聊这道题吧~


1. 看到这道题目时我想到了什么,以及如何运用现实案例讲解

你有没有想过,生活中很多事情就像遍历一棵树一样。比如说,我们在整理一个家族谱系图时,可能会按照某种顺序(比如先看左支的子孙,再看自己,再看右支的子孙)去记录每个人的信息。这就是中序遍历的思想:左子树 -> 根节点 -> 右子树

再举个贴近算法的例子:中序遍历在二叉搜索树(BST)中特别有用,因为它会按照从小到大的顺序输出节点值。想象你在一家书店管理图书库存,图书按编号从小到大排列在书架上(类似BST),你想按顺序检查每本书的状态,中序遍历就是你检查的顺序。

业务场景:这道题在实际业务中有很多应用,比如:

  • 数据库索引:B树或BST的中序遍历可以用来按顺序访问数据。
  • 文件系统:遍历文件夹和文件的层级结构。
  • 表达式解析:中序遍历可以用来解析数学表达式(比如中缀表达式)。

所以,理解了中序遍历,你就掌握了树结构中一个非常核心的操作!


2. 解法分析:至少3种解法,最优解法和最快解法

解法1:递归(Recursion)——最直观且代码简洁

思路:用递归的方式实现中序遍历。按照“左->根->右”的顺序,先递归遍历左子树,然后访问当前节点,最后递归遍历右子树。代码非常直观,符合人类思维习惯。

  • 时间复杂度:O(n),n是树中节点的数量,每个节点只访问一次。
  • 空间复杂度:O(h),h是树的高度,用于递归调用栈,平均情况下为O(log n),最坏情况下为O(n)(树退化为链表)。
解法2:迭代(Iteration with Stack)——用栈模拟递归

思路:用一个栈来模拟递归过程。先将根节点及其所有左子节点压入栈中,然后弹出节点并访问,再处理右子节点。通过栈来手动维护遍历顺序,避免递归的调用栈开销。

  • 时间复杂度:O(n),每个节点只入栈和出栈一次。
  • 空间复杂度:O(h),h是树的高度,用于栈空间,平均O(log n),最坏O(n)。
解法3:Morris遍历——空间复杂度O(1)的进阶方法

思路:Morris遍历是一种不需要额外栈或递归空间的方法,通过临时修改树的结构(建立前驱节点到当前节点的链接)来实现中序遍历。虽然空间复杂度为O(1),但会暂时改变树结构,代码较复杂,不适合需要保持树结构不变的场景。

  • 时间复杂度:O(n),每个节点会被访问常数次。
  • 空间复杂度:O(1),不使用额外空间。
最优解法和最快解法
  • 最优解法:如果是初学者或代码可读性优先,推荐递归解法,因为它最直观,代码最简洁,容易理解和维护。如果对空间有要求,可以选择迭代解法,在实际工程中也更常用。Morris遍历虽然空间最优,但代码复杂且会修改树结构,通常不作为首选。
  • 最快解法:在实际运行中,迭代解法通常是最快的,因为它避免了递归的函数调用开销,直接用栈操作,效率更高。

迭代解法模板(Java,用栈实现)

public List<Integer> inorderTraversal(TreeNode root) {List<Integer> result = new ArrayList<>();Stack<TreeNode> stack = new Stack<>();TreeNode current = root;while (current != null || !stack.isEmpty()) {// 将当前节点及其所有左子节点压入栈while (current != null) {stack.push(current);current = current.left;}// 弹出栈顶节点并访问current = stack.pop();result.add(current.val);// 处理右子节点current = current.right;}return result;
}

3. 迭代解法(用栈)的流程图(Mermaid)

你画一个流程图,帮你更清晰地理解迭代解法的步骤。咱们用 Mermaid 图来表示:

开始
初始化结果列表和栈
设置当前节点为root
当前节点不为空 或 栈不为空?
返回结果列表
当前节点不为空?
将当前节点压入栈
当前节点 = 当前节点的左子节点
弹出栈顶节点
将栈顶节点值加入结果列表
当前节点 = 栈顶节点的右子节点

流程解释

  1. 初始化一个结果列表和一个栈,设置当前节点为根节点。
  2. 只要当前节点不为空或栈不为空,就继续循环。
  3. 如果当前节点不为空,就把当前节点及其所有左子节点压入栈。
  4. 如果当前节点为空,从栈中弹出节点,访问该节点(加入结果列表),然后将当前节点设为该节点的右子节点。
  5. 重复上述步骤,直到栈为空且当前节点为空,返回结果。

类似题目推荐

  • LeetCode 144: Binary Tree Preorder Traversal(二叉树的前序遍历)
    • 这道题是遍历树的另一种顺序:根->左->右。核心思想和中序遍历类似,你可以试着用今天学到的递归和迭代方法去解决,相信你一定能搞定!
  • LeetCode 145: Binary Tree Postorder Traversal(二叉树的后序遍历)
    • 后序遍历的顺序是左->右->根,稍微有点挑战,但用栈的方法也能实现,试试看吧!

5. 完整 Java 代码(递归和迭代两种方法)

递归和迭代两种方法的代码都给你贴出来,里面有详细的注释,方便你理解:

递归解法(最直观)
class Solution {public List<Integer> inorderTraversal(TreeNode root) {List<Integer> result = new ArrayList<>();inorderHelper(root, result);return result;}private void inorderHelper(TreeNode node, List<Integer> result) {if (node == null) {return;}// 先遍历左子树inorderHelper(node.left, result);// 访问当前节点result.add(node.val);// 再遍历右子树inorderHelper(node.right, result);}
}
迭代解法(用栈,效率更高)
class Solution {public List<Integer> inorderTraversal(TreeNode root) {List<Integer> result = new ArrayList<>();Stack<TreeNode> stack = new Stack<>();TreeNode current = root;// 只要当前节点不为空或栈不为空,继续遍历while (current != null || !stack.isEmpty()) {// 将当前节点及其所有左子节点压入栈while (current != null) {stack.push(current);current = current.left;}// 弹出栈顶节点并访问current = stack.pop();result.add(current.val);// 处理右子节点current = current.right;}return result;}
}
TreeNode 定义(供参考)
public class TreeNode {int val;TreeNode left;TreeNode right;TreeNode() {}TreeNode(int val) { this.val = val; }TreeNode(int val, TreeNode left, TreeNode right) {this.val = val;this.left = left;this.right = right;}
}

6. 算法知识点与学习资料

知识点
  1. 二叉树遍历的三种顺序
    • 前序遍历:根->左->右
    • 中序遍历:左->根->右
    • 后序遍历:左->右->根
  2. 递归与迭代的对比
    • 递归:代码简洁,符合直觉,但有调用栈开销,可能导致栈溢出。
    • 迭代:用栈模拟递归,效率更高,适合大数据场景。
  3. Morris遍历
    • 一种不使用额外空间的遍历方法,通过临时修改树结构实现,适合进阶学习。
  4. 时间与空间复杂度分析
    • 树的遍历通常时间复杂度为O(n),空间复杂度取决于树的高度。
学习资料
  1. 书籍推荐
    • 《算法导论》(Introduction to Algorithms):深入讲解树的遍历和复杂度分析。
    • 《数据结构与算法分析》(Data Structures and Algorithms in Java):适合用Java学习数据结构。
  2. 在线资源
    • LeetCode 讨论区:可以看看其他人的解法和思路,尤其是Morris遍历的讲解。
    • 网易云课堂或B站的算法视频:搜索“二叉树遍历”或“中序遍历”,有很多直观的动画演示。
  3. 实践平台
    • LeetCode:多刷树的遍历相关题目,比如前序、后序遍历。
    • HackerRank:有许多树相关的挑战题,可以巩固基础。

http://www.xdnf.cn/news/6256.html

相关文章:

  • 海信璀璨505U6真空冰箱闪耀“国家德比”
  • 从零开始完成“大模型在牙科诊所青少年拉新系统中RAG与ReACT功能实现”的路线图
  • 【Python】对象生命周期全解析
  • 【Python-Day 13】Python 元组 (Tuple) 详解:从创建、操作到高级应用场景一网打尽
  • springboot AOP 接口限流(基于IP的接口限流和黑白名单)
  • 万字解析:Java字符串
  • vue3基础学习(上) [简单标签] (vscode)
  • “redis 目标计算机积极拒绝,无法连接” 解决方法,每次开机启动redis
  • 图表制作-基础饼图
  • Nightingale监控系统介绍与部署(可离线部署)
  • sql server 2019 将单用户状态修改为多用户状态
  • map和unordered_map
  • 七部门:设立“国家创业投资引导基金”,优先支持取得关键核心技术突破的科技型企业上市融资
  • libmemcached库api接口讲解零
  • 使用frp实现客户端开机自启(含静默运行脚本)
  • IEEE PRMVAI 2025 “人工智能的应用“分论坛
  • 在 Rocky Linux 上手动安装 zsh
  • 龙虎榜——20250514
  • Postman接口测试
  • 操作系统实验 实验4 页面置换算法
  • python库sqlalchemy
  • 现代计算机图形学Games101入门笔记(八)
  • K8S redis 部署
  • 火线、零线、地线
  • 【HALCON】 HALCON 教程:正确使用 `get_dict_tuple` 获取字典内容
  • win11 VSCode 强制弹窗微软登录
  • 【数据管理平台测试文档】
  • 40-canvas中文字的横向对齐方式
  • CSS 锚点滑动效果的技术
  • NDM:高效全能的下载工具