(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]。
相关推荐
wuweijianlove3 小时前
算法性能的渐近与非渐近行为对比的技术4
算法
_dindong3 小时前
cf1091div2 C.Grid Covering(数论)
c++·算法
AI成长日志3 小时前
【Agentic RL】1.1 什么是Agentic RL:从传统RL到智能体学习
人工智能·学习·算法
黎阳之光4 小时前
黎阳之光:视频孪生领跑者,铸就中国数字科技全球竞争力
大数据·人工智能·算法·安全·数字孪生
skywalker_114 小时前
力扣hot100-3(最长连续序列),4(移动零)
数据结构·算法·leetcode
6Hzlia4 小时前
【Hot 100 刷题计划】 LeetCode 17. 电话号码的字母组合 | C++ 回溯算法经典模板
c++·算法·leetcode
wfbcg5 小时前
每日算法练习:LeetCode 209. 长度最小的子数组 ✅
算法·leetcode·职场和发展
_日拱一卒5 小时前
LeetCode:除了自身以外数组的乘积
数据结构·算法·leetcode
计算机安禾5 小时前
【数据结构与算法】第36篇:排序大总结:稳定性、时间复杂度与适用场景
c语言·数据结构·c++·算法·链表·线性回归·visual studio
SatVision炼金士5 小时前
合成孔径雷达干涉测量(InSAR)沉降监测算法体系
算法