题目
给定一个二叉树的根节点 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 算法中,我们靠动态修改树的结构(建立临时连接)来记录。
核心步骤拆解:
算法主要处理三种情况:
- 没有左孩子 (root.left == null):
-
既然没有左边,那就该轮到打印当前节点了。
-
打印完后,直接去右边 (root = root.right)。
-
注意:这里的"右边"可能是真的右子树,也可能是一条指向祖先节点的"线索"(通过之前建立的连接)。
- 有左孩子,且是"第一次"到达该节点:
-
我们需要去遍历左子树,但去之前要给自己留条后路。
-
找前驱 (Predecessor): 也就是左子树里最右边的那个节点。在标准的中序遍历中,这个前驱节点的下一个节点就应该是当前的 root。
-
搭桥 (Threading): 我们把前驱节点的 right 指针指向当前 root (predecessor.right = root)。
-
然后放心地向左走 (root = root.left)。
- 有左孩子,且是"第二次"到达该节点:
-
当我们遍历完左子树后,最后一步会通过刚才搭的那个"桥"回到当前的 root。
-
此时,我们再次寻找前驱,会发现 predecessor.right == root。这就意味着我们是"穿越"回来的。
-
拆桥: 把 predecessor.right 改回 null,恢复树的原貌。
-
处理当前节点: 将 root.val 加入结果集。
-
向右走: 左边和中间都处理完了,现在去右边 (root = root.right)。
举例演示
假设树结构为:2 -> 1 (左), 3 (右)
- 开始在 2: 有左子树 (1)。找到 1 是前驱。
-
把 1 的 right 指向 2(搭桥)。
-
Current 移到 1。
- 现在在 1: 没有左子树。
-
记录 1。
-
Current 移到 right。因为刚才搭了桥,1 的 right 指向 2。所以 Current 回到了 2。
- 回到 2: 有左子树 (1)。再次找前驱 1。
-
发现 1 的 right 已经指向 2 了(说明是第二次来)。
-
记录 2。
-
断开桥 (1 的 right 设为 null)。
-
Current 移到 2 的 right (即 3)。
- 现在在 3: 没有左子树。
-
记录 3。
-
Current 移到 right (null)。
- 结束。 结果:[1, 2, 3]。