(leetcode)力扣100 36二叉树的中序遍历(迭代递归)

题目

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

数据范围

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

-100 <= Node.val <= 100

测试用例

示例1

java 复制代码
输入:root = [1,null,2,3]
输出:[1,3,2]

示例2

java 复制代码
输入:root = []
输出:[]

示例3

java 复制代码
输入:root = [1]
输出:[1]

题解1(迭代 时空On)

java 复制代码
/**
 * Definition for a binary tree node.
 * 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;
 *     }
 * }
 */
class Solution {
    public List<Integer> inorderTraversal(TreeNode root) {
       List<Integer> res=new LinkedList<>();
       Deque<TreeNode> deque=new LinkedList<>();
       while(root!=null||!deque.isEmpty()){
            while(root!=null){
                deque.push(root);
                root=root.left;
            }
            root=deque.poll();
            res.add(root.val);
            root=root.right;
       }
       return res;
    }
}

题解2 (递归 时空on)

java 复制代码
class Solution {
    public List<Integer> inorderTraversal(TreeNode root) {
        List<Integer> res = new ArrayList<Integer>();
        inorder(root, res);
        return res;
    }

    public void inorder(TreeNode root, List<Integer> res) {
        if (root == null) {
            return;
        }
        inorder(root.left, res);
        res.add(root.val);
        inorder(root.right, res);
    }
}

题解3 (Morris中序遍历,时间On,空间O1 )

java 复制代码
/**
 * Definition for a binary tree node.
 * ... (省略 TreeNode 定义) ...
 */
class Solution {
    public List<Integer> inorderTraversal(TreeNode root) {
        List<Integer> res = new LinkedList<>();
        TreeNode predecessor = null; // 用于存储当前节点的前驱节点(即左子树中最右边的节点)

        while (root != null) {
            // 情况 1: 如果当前节点有左子树
            if (root.left != null) {
                // 1.1 找到当前节点的前驱节点 (predecessor)
                predecessor = root.left;
                // 不断向右走,直到右节点为空,或者右节点已经指向了当前 root(说明线索已建立)
                while (predecessor.right != null && predecessor.right != root) {
                    predecessor = predecessor.right;
                }

                // 1.2 判断是第一次来到这里,还是第二次来到这里
                
                // 如果前驱节点的右指针为空,说明这是第一次访问该节点
                if (predecessor.right == null) {
                    predecessor.right = root; // 【建立线索】:让前驱节点的右指针指向当前节点
                    root = root.left;         // 当前节点向左移动
                } 
                // 如果前驱节点的右指针已经指向当前 root,说明左子树已经遍历完了
                else {
                    res.add(root.val);        // 【中序遍历关键】:左子树处理完,记录当前节点的值
                    predecessor.right = null; // 【断开线索】:恢复树的原始结构
                    root = root.right;        // 当前节点向右移动
                }
            } 
            // 情况 2: 如果当前节点没有左子树
            else {
                res.add(root.val); // 【中序遍历关键】:直接记录当前节点的值
                root = root.right; // 向右移动(如果有线索,会通过线索回到上层节点)
            }
        }

        return res;
    }
}

思路

这道题是最简单的二叉树中序遍历,就不过多讲解普通的迭代算法与递归算法了,记住就行了,关于morris算法。

Morris 遍历的核心思想是利用树中大量的空闲指针(叶子节点的左右孩子空指针)。对于中序遍历(左 -> 根 -> 右),我们需要在遍历完左子树后,能够自动回到"根"节点。

在递归或栈迭代中,我们靠栈来记录"回家的路"。在 Morris 算法中,我们靠动态修改树的结构(建立临时连接)来记录。

核心步骤拆解:

算法主要处理三种情况:

  1. 没有左孩子 (root.left == null):
  • 既然没有左边,那就该轮到打印当前节点了。

  • 打印完后,直接去右边 (root = root.right)。

  • 注意:这里的"右边"可能是真的右子树,也可能是一条指向祖先节点的"线索"(通过之前建立的连接)。

  1. 有左孩子,且是"第一次"到达该节点:
  • 我们需要去遍历左子树,但去之前要给自己留条后路。

  • 找前驱 (Predecessor): 也就是左子树里最右边的那个节点。在标准的中序遍历中,这个前驱节点的下一个节点就应该是当前的 root。

  • 搭桥 (Threading): 我们把前驱节点的 right 指针指向当前 root (predecessor.right = root)。

  • 然后放心地向左走 (root = root.left)。

  1. 有左孩子,且是"第二次"到达该节点:
  • 当我们遍历完左子树后,最后一步会通过刚才搭的那个"桥"回到当前的 root。

  • 此时,我们再次寻找前驱,会发现 predecessor.right == root。这就意味着我们是"穿越"回来的。

  • 拆桥: 把 predecessor.right 改回 null,恢复树的原貌。

  • 处理当前节点: 将 root.val 加入结果集。

  • 向右走: 左边和中间都处理完了,现在去右边 (root = root.right)。

举例演示

假设树结构为:2 -> 1 (左), 3 (右)

  1. 开始在 2: 有左子树 (1)。找到 1 是前驱。
  • 把 1 的 right 指向 2(搭桥)。

  • Current 移到 1。

  1. 现在在 1: 没有左子树。
  • 记录 1。

  • Current 移到 right。因为刚才搭了桥,1 的 right 指向 2。所以 Current 回到了 2。

  1. 回到 2: 有左子树 (1)。再次找前驱 1。
  • 发现 1 的 right 已经指向 2 了(说明是第二次来)。

  • 记录 2。

  • 断开桥 (1 的 right 设为 null)。

  • Current 移到 2 的 right (即 3)。

  1. 现在在 3: 没有左子树。
  • 记录 3。

  • Current 移到 right (null)。

  1. 结束。 结果:[1, 2, 3]。
相关推荐
wangwangmoon_light8 小时前
1.1 LeetCode总结(线性表)_枚举技巧
算法·leetcode·哈希算法
mit6.8249 小时前
几何|阻碍链
算法
有一个好名字9 小时前
力扣-小行星碰撞
算法·leetcode·职场和发展
MM_MS9 小时前
Halcon图像锐化和图像增强、窗口的相关算子
大数据·图像处理·人工智能·opencv·算法·计算机视觉·视觉检测
lamentropetion9 小时前
E - Equal Tree Sums CF1656E
算法
代码游侠9 小时前
应用——智能配电箱监控系统
linux·服务器·数据库·笔记·算法·sqlite
2301_800895109 小时前
hh的蓝桥杯每日一题--前缀和
职场和发展·蓝桥杯
Xの哲學9 小时前
Linux Platform驱动深度剖析: 从设计思想到实战解析
linux·服务器·网络·算法·边缘计算
逑之9 小时前
C语言笔记11:字符函数和字符串函数
c语言·笔记·算法