【LeetHOT100】二叉树的中序遍历——Java多解法详解

一、题目描述

94. 二叉树的中序遍历

给定一个二叉树的根节点 root,返回它的 中序遍历

中序遍历 的定义:按照 左子树 → 根节点 → 右子树 的顺序遍历二叉树。

示例 1:

输入:root = [1,null,2,3]

输出:[1,3,2]

示例 2:

输入:root = []

输出:[]

示例 3:

输入:root = [1]

输出:[1]

提示:

  • 树中节点数目在范围 [0, 100]

  • -100 <= Node.val <= 100

进阶: 递归算法很简单,你可以通过迭代算法完成吗?

二、解题思路概览

二叉树的中序遍历是树的三种基本遍历(前序、中序、后序)之一。常见解法有三种:

解法 时间复杂度 空间复杂度 特点 面试推荐
递归 O(n) O(n) (最坏) 代码简洁,思路清晰 ⭐⭐⭐⭐⭐
迭代(栈) O(n) O(n) 模拟递归,面试常考 ⭐⭐⭐⭐⭐
Morris 遍历 O(n) O(1) 空间最优,实现稍复杂 ⭐⭐⭐

三、解法一:递归(最简洁)

3.1 核心思路

递归是最直观的实现方式:

  1. 先遍历左子树

  2. 访问根节点

  3. 再遍历右子树

3.2 代码实现

java

复制代码
class Solution {
    private List<Integer> result = new ArrayList<>();
    
    public List<Integer> inorderTraversal(TreeNode root) {
        dfs(root);
        return result;
    }
    
    private void dfs(TreeNode node) {
        if (node == null) {
            return;
        }
        dfs(node.left);          // 1. 遍历左子树
        result.add(node.val);    // 2. 访问根节点
        dfs(node.right);         // 3. 遍历右子树
    }
}

3.3 图解示例

root = [1,null,2,3] 为例(树的结构):

text

复制代码
    1
     \
      2
     /
    3

递归过程:

text

复制代码
dfs(1):
  → dfs(1.left=null): 返回
  → 结果添加 1
  → dfs(1.right=2):
      → dfs(2.left=3):
          → dfs(3.left=null): 返回
          → 结果添加 3
          → dfs(3.right=null): 返回
      → 结果添加 2
      → dfs(2.right=null): 返回
  → 返回

结果顺序: [1, 3, 2]

3.4 复杂度分析

  • 时间复杂度:O(n),每个节点恰好被访问一次

  • 空间复杂度:O(n),最坏情况下(单链表树)递归栈深度为 n

四、解法二:迭代(显式栈)⭐⭐⭐⭐⭐

4.1 核心思路

用栈模拟递归调用过程:

  1. 从根节点开始,将所有左子节点压入栈

  2. 弹出栈顶节点,访问它

  3. 将指针移动到栈顶节点的右子节点,重复步骤1

4.2 代码实现

java

复制代码
class Solution {
    public List<Integer> inorderTraversal(TreeNode root) {
        List<Integer> result = new ArrayList<>();
        Deque<TreeNode> stack = new ArrayDeque<>();
        TreeNode cur = root;
        
        while (cur != null || !stack.isEmpty()) {
            // 1. 不断向左走,将所有左子节点压入栈
            while (cur != null) {
                stack.push(cur);
                cur = cur.left;
            }
            
            // 2. 弹出节点并访问
            cur = stack.pop();
            result.add(cur.val);
            
            // 3. 转向右子树
            cur = cur.right;
        }
        
        return result;
    }
}

4.3 图解示例

root = [1,null,2,3] 为例:

text

复制代码
步骤1: cur=1, stack=[1]
步骤2: cur=1.left=null,退出内层循环
步骤3: 弹出1,访问1 → result=[1], cur=1.right=2
步骤4: cur=2, while循环: stack=[2]
步骤5: cur=2.left=3, stack=[2,3]
步骤6: cur=3.left=null,退出内层循环
步骤7: 弹出3,访问3 → result=[1,3], cur=3.right=null
步骤8: cur=null, stack=[2]
步骤9: 弹出2,访问2 → result=[1,3,2], cur=2.right=null
步骤10: cur=null, stack为空,结束

4.4 复杂度分析

  • 时间复杂度:O(n),每个节点入栈出栈各一次

  • 空间复杂度:O(n),栈最多存储树的高度个节点

五、解法三:Morris 遍历(O(1) 空间)

5.1 核心思路

Morris 遍历利用树的空闲指针(叶节点的左右空指针)来实现 O(1) 空间的中序遍历。

核心思想

  1. 建立临时链接:找到当前节点的前驱节点(左子树的最右节点),将其右指针指向当前节点

  2. 遍历左子树

  3. 访问当前节点

  4. 恢复原树结构(断开临时链接)

  5. 遍历右子树

5.2 代码实现

java

复制代码
class Solution {
    public List<Integer> inorderTraversal(TreeNode root) {
        List<Integer> result = new ArrayList<>();
        TreeNode cur = root;
        
        while (cur != null) {
            if (cur.left == null) {
                // 没有左子树,直接访问当前节点
                result.add(cur.val);
                cur = cur.right;  // 转向右子树
            } else {
                // 找到左子树的最右节点(前驱节点)
                TreeNode predecessor = cur.left;
                while (predecessor.right != null && predecessor.right != cur) {
                    predecessor = predecessor.right;
                }
                
                if (predecessor.right == null) {
                    // 建立临时链接
                    predecessor.right = cur;
                    cur = cur.left;  // 继续遍历左子树
                } else {
                    // 临时链接已存在,说明左子树已遍历完
                    predecessor.right = null;  // 断开临时链接
                    result.add(cur.val);       // 访问当前节点
                    cur = cur.right;           // 转向右子树
                }
            }
        }
        
        return result;
    }
}

5.3 图解示例

root = [1,2,3,4,5] 为例:

text

复制代码
原始树:
      1
     / \
    2   3
   / \
  4   5

步骤1:cur=1, 找到前驱=5(2的右子树最右)
  将5.right指向1
   cur → 2

步骤2:cur=2, 找到前驱=4
  将4.right指向2
   cur → 4

步骤3:cur=4, 左子树为空
  访问4
   cur → 4.right(指向2)

步骤4:cur=2, 前驱的right已指向2
  断开4.right
  访问2
   cur → 2.right=5

步骤5:cur=5, 左子树为空
  访问5
   cur → 5.right(指向1)

步骤6:cur=1, 前驱的right已指向1
  断开5.right
  访问1
   cur → 1.right=3

...后续遍历右子树

5.4 复杂度分析

  • 时间复杂度:O(n),每个节点最多被访问两次

  • 空间复杂度:O(1),只使用了常数个指针

5.5 Morris 遍历的特点

优点 缺点
空间复杂度 O(1) 实现较复杂,容易出错
不占用额外栈空间 会临时修改树的结构(但最终会恢复)
适合对空间敏感的嵌入式场景 理解难度较高

六、解法对比与总结

方法 时间复杂度 空间复杂度 代码复杂度 面试推荐
递归 O(n) O(n) 极简 ⭐⭐⭐⭐⭐
迭代(栈) O(n) O(n) 中等 ⭐⭐⭐⭐⭐
Morris O(n) O(1) 较复杂 ⭐⭐⭐

七、面试建议

7.1 应该用哪个?

场景 推荐解法
面试标准答案 递归迭代(栈)
被要求写迭代 显式栈版本
被问是否能 O(1) 空间 Morris 遍历
快速实现 递归

7.2 常见错误

  1. 递归解法

    • 忘记处理 null 边界条件

    • add 的顺序写错(前序/后序混淆)

  2. 迭代解法

    • 忘记将左子节点全部入栈

    • 访问节点后忘记将 cur 指向右子节点

    • 栈中元素处理顺序错误

  3. Morris 遍历

    • 循环条件写错

    • 忘记恢复前驱节点的 right 指针

    • 前驱查找时没有判断 predecessor.right != cur

7.3 三种遍历的统一模板

如果你需要同时掌握前序、中序、后序的迭代写法,可以参考这个模式:

java

复制代码
// 中序遍历的迭代模板
public List<Integer> inorderTraversal(TreeNode root) {
    List<Integer> result = new ArrayList<>();
    Deque<TreeNode> stack = new ArrayDeque<>();
    TreeNode cur = root;
    
    while (cur != null || !stack.isEmpty()) {
        if (cur != null) {
            stack.push(cur);
            cur = cur.left;          // 中序:先左
        } else {
            cur = stack.pop();
            result.add(cur.val);      // 中序:再中
            cur = cur.right;          // 中序:后右
        }
    }
    return result;
}

八、相关链接

相关推荐
jay神1 小时前
基于SpringBoot的宠物生命周期信息管理系统
java·数据库·spring boot·后端·web开发·宠物·管理系统
万亿少女的梦1682 小时前
基于SpringBoot的在线考试管理系统设计与实现
java·spring boot·后端
m0_738120722 小时前
渗透测试——Djinn1靶场详细渗透提权过程讲解(绕过黑名单限制,命令执行反弹shell,pyc反编译,代码白盒分析,python沙盒逃逸)
开发语言·python·php
一勺菠萝丶2 小时前
如何在 Linux 服务器上使用 Speedtest 官方 CLI 测试带宽(小白教程)
java·服务器·前端
web守墓人2 小时前
【go语言】go语言实现go-torch, 完成Lenet-5的搭建,训练,以及pth和onnx模型导出
开发语言·后端·golang
TEC_INO2 小时前
Linux50:ROCKX+RV1126视频流检测人脸
开发语言·前端·javascript
平凡但不平庸的码农2 小时前
Go 语言常用标准库详解
开发语言·后端·golang
下载居2 小时前
Node.js(Javascript运行环境) 26.1
开发语言·javascript·node.js
范什么特西2 小时前
第一个Mybatis
java·开发语言·mybatis