二叉树(中序遍历)

嘿,欢迎来到小巫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:有许多树相关的挑战题,可以巩固基础。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值