leetcode
leetcode 51 ~ 100
二叉树的中序遍历

二叉树的中序遍历

难度:

标签:

题目描述

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

 

示例 1:

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

示例 2:

输入:root = []
输出:[]

示例 3:

输入:root = [1]
输出:[1]

 

提示:

  • 树中节点数目在范围 [0, 100]
  • -100 <= Node.val <= 100

 

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

代码结果

运行时间: 36 ms, 内存: 14.8 MB


/*
 * 题目思路:
 * 使用Java Stream进行中序遍历相对复杂,但可以通过递归生成流并合并。
 * 1. 使用递归生成左子树的流。
 * 2. 使用流连接当前节点的值。
 * 3. 使用递归生成右子树的流。
 */
import java.util.*;
import java.util.stream.*;
 
public class Solution {
    public List<Integer> inorderTraversal(TreeNode root) {
        return inorder(root).collect(Collectors.toList());
    }
 
    private Stream<Integer> inorder(TreeNode root) {
        if (root == null) return Stream.empty();
        return Stream.concat(
            Stream.concat(
                inorder(root.left),
                Stream.of(root.val)
            ),
            inorder(root.right)
        );
    }
 
    // 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;
        }
    }
}

解释

方法:

这个题解使用了迭代的方式来实现二叉树的中序遍历。具体思路是:用一个栈来保存节点,先将根节点压入栈中,然后进入循环。在每次循环中,如果当前节点不为空,就将其压入栈中,并将当前节点更新为其左子节点,直到当前节点为空。当前节点为空时,说明已经到达了最左边的叶子节点,此时从栈中弹出一个节点,将其值加入结果列表中,并将当前节点更新为其右子节点。重复这个过程,直到当前节点和栈都为空,遍历结束。

时间复杂度:

O(n)

空间复杂度:

O(n)

代码细节讲解

🦆
在迭代方法中,为什么首先要不断地将左子节点压入栈中,直到没有左子节点?这样做的目的是什么?
在二叉树的中序遍历中,遵循的顺序是左-根-右。因此,首先需要访问到最左边的节点,确保按照正确的顺序处理每个节点。通过不断将左子节点压入栈中,我们能够先到达并处理树的最左侧,即最深层的左子节点,然后再依次向上回溯到父节点和右子节点。这种方式确保了每个节点都是在其所有左子节点被处理之后才被访问。
🦆
在这种迭代方法中,栈的作用是什么?它如何帮助完成中序遍历?
栈在这种迭代方法中扮演了一个关键角色,主要用于保存暂时无法处理的节点(因为它们的左子节点还未完全访问)。栈帮助维持访问顺序,确保在处理完所有左子节点后能够回到当前节点,然后转到右子节点。这种后进先出(LIFO)的特性正是中序遍历所需的,因为它确保了节点访问的顺序符合左-根-右的要求。
🦆
如果在二叉树中存在值相同的节点,这种迭代方法是否还能正确地返回所有节点的值?会不会有遗漏或重复?
这种迭代方法处理的是节点的物理位置而非节点的值,因此即使存在值相同的节点,遍历过程中每个节点都会被访问一次,并按照中序遍历的顺序返回其值。算法依赖于树的结构,而不是节点值的唯一性,所以不会有遗漏或重复的问题,每个节点都会被精确地访问并记录一次。
🦆
在迭代过程中,如何确保每个节点只被访问一次?
在迭代过程中,节点首先被推入栈中,然后在到达最左侧节点后开始逐一弹出并访问。每个节点在被访问(即加入结果列表)之后,会将其右子节点设置为当前节点以继续遍历过程。这一过程确保了每个节点在整个遍历中仅被访问一次,因为一旦节点被弹出栈并处理,就不会再次被压入栈。

相关问题

验证二叉搜索树

给你一个二叉树的根节点 root ,判断其是否是一个有效的二叉搜索树。

有效 二叉搜索树定义如下:

  • 节点的左子树只包含 小于 当前节点的数。
  • 节点的右子树只包含 大于 当前节点的数。
  • 所有左子树和右子树自身必须也是二叉搜索树。

 

示例 1:

输入:root = [2,1,3]
输出:true

示例 2:

输入:root = [5,1,4,null,null,3,6]
输出:false
解释:根节点的值是 5 ,但是右子节点的值是 4 。

 

提示:

  • 树中节点数目范围在[1, 104]
  • -231 <= Node.val <= 231 - 1

二叉树的前序遍历

给你二叉树的根节点 root ,返回它节点值的 前序 遍历。

 

示例 1:

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

示例 2:

输入:root = []
输出:[]

示例 3:

输入:root = [1]
输出:[1]

示例 4:

输入:root = [1,2]
输出:[1,2]

示例 5:

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

 

提示:

  • 树中节点数目在范围 [0, 100]
  • -100 <= Node.val <= 100

 

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

二叉树的后序遍历

给你一棵二叉树的根节点 root ,返回其节点值的 后序遍历

 

示例 1:

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

示例 2:

输入:root = []
输出:[]

示例 3:

输入:root = [1]
输出:[1]

 

提示:

  • 树中节点的数目在范围 [0, 100]
  • -100 <= Node.val <= 100

 

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

二叉搜索树迭代器

实现一个二叉搜索树迭代器类BSTIterator ,表示一个按中序遍历二叉搜索树(BST)的迭代器:
  • BSTIterator(TreeNode root) 初始化 BSTIterator 类的一个对象。BST 的根节点 root 会作为构造函数的一部分给出。指针应初始化为一个不存在于 BST 中的数字,且该数字小于 BST 中的任何元素。
  • boolean hasNext() 如果向指针右侧遍历存在数字,则返回 true ;否则返回 false
  • int next()将指针向右移动,然后返回指针处的数字。

注意,指针初始化为一个不存在于 BST 中的数字,所以对 next() 的首次调用将返回 BST 中的最小元素。

你可以假设 next() 调用总是有效的,也就是说,当调用 next() 时,BST 的中序遍历中至少存在一个下一个数字。

 

示例:

输入
["BSTIterator", "next", "next", "hasNext", "next", "hasNext", "next", "hasNext", "next", "hasNext"]
[[[7, 3, 15, null, null, 9, 20]], [], [], [], [], [], [], [], [], []]
输出
[null, 3, 7, true, 9, true, 15, true, 20, false]

解释
BSTIterator bSTIterator = new BSTIterator([7, 3, 15, null, null, 9, 20]);
bSTIterator.next();    // 返回 3
bSTIterator.next();    // 返回 7
bSTIterator.hasNext(); // 返回 True
bSTIterator.next();    // 返回 9
bSTIterator.hasNext(); // 返回 True
bSTIterator.next();    // 返回 15
bSTIterator.hasNext(); // 返回 True
bSTIterator.next();    // 返回 20
bSTIterator.hasNext(); // 返回 False

 

提示:

  • 树中节点的数目在范围 [1, 105]
  • 0 <= Node.val <= 106
  • 最多调用 105hasNextnext 操作

 

进阶:

  • 你可以设计一个满足下述条件的解决方案吗?next()hasNext() 操作均摊时间复杂度为 O(1) ,并使用 O(h) 内存。其中 h 是树的高度。

二叉搜索树中第K小的元素

给定一个二叉搜索树的根节点 root ,和一个整数 k ,请你设计一个算法查找其中第 k 个最小元素(从 1 开始计数)。

 

示例 1:

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

示例 2:

输入:root = [5,3,6,2,4,null,null,1], k = 3
输出:3

 

 

提示:

  • 树中的节点数为 n
  • 1 <= k <= n <= 104
  • 0 <= Node.val <= 104

 

进阶:如果二叉搜索树经常被修改(插入/删除操作)并且你需要频繁地查找第 k 小的值,你将如何优化算法?

最接近的二叉搜索树值 II

最接近的二叉搜索树值 II

二叉搜索树中的中序后继

二叉搜索树中的中序后继

将二叉搜索树转化为排序的双向链表

将二叉搜索树转化为排序的双向链表

二叉搜索树节点最小距离

给你一个二叉搜索树的根节点 root ,返回 树中任意两不同节点值之间的最小差值

差值是一个正数,其数值等于两值之差的绝对值。

 

示例 1:

输入:root = [4,2,6,1,3]
输出:1

示例 2:

输入:root = [1,0,48,null,null,12,49]
输出:1

 

提示:

  • 树中节点的数目范围是 [2, 100]
  • 0 <= Node.val <= 105

 

注意:本题与 530:https://leetcode-cn.com/problems/minimum-absolute-difference-in-bst/ 相同