leetcode
leetcode 201 ~ 250
二叉搜索树中第K小的元素

二叉搜索树中第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 小的值,你将如何优化算法?

代码结果

运行时间: 60 ms, 内存: 18.9 MB


/**
 * 思路:
 * 利用二叉搜索树的中序遍历的特性,用Java Stream API获取中序遍历的所有元素
 * 然后直接获取第 k 个最小元素。
 */
import java.util.stream.Collectors;
import java.util.List;
 
public class Solution {
    public int kthSmallest(TreeNode root, int k) {
        List<Integer> sortedElements = inorder(root).stream().collect(Collectors.toList());
        return sortedElements.get(k - 1);
    }
    
    private List<Integer> inorder(TreeNode root) {
        if (root == null) return List.of();
        List<Integer> left = inorder(root.left);
        List<Integer> right = inorder(root.right);
        return Stream.concat(Stream.concat(left.stream(), Stream.of(root.val)), right.stream()).collect(Collectors.toList());
    }
}
 

解释

方法:

这个题解使用了迭代的方式进行中序遍历二叉搜索树。中序遍历二叉搜索树可以得到一个升序排列的序列,因此第k小的元素就是中序遍历序列中的第k个元素。具体做法是:用一个栈来模拟递归的过程,首先不断地将根节点的左子树压入栈中,直到左子树为空;然后弹出栈顶元素并将k减1,如果此时k等于0,说明已经找到了第k小的元素,直接返回该元素的值;否则继续遍历该元素的右子树。重复这个过程直到找到第k小的元素或遍历完整棵树。

时间复杂度:

O(h+k),其中h为树的高度

空间复杂度:

O(h),其中h为树的高度

代码细节讲解

🦆
在算法中,如何处理树中可能存在的相同值节点?这是否会影响第k小元素的查找?
在二叉搜索树中,即使存在相同值的节点,中序遍历依然可以正确地按顺序访问每个节点。算法中的中序遍历会按照左-根-右的顺序访问这些节点,包括重复值节点。这意味着即便是具有相同值的节点,它们也会根据在树中的位置被逐一考虑。因此,尽管存在相同的值,第k小的元素的查找仍然是准确的,每个节点都会被按照中序顺序计算。
🦆
为什么选择迭代方法而不是递归方法来进行中序遍历?两者在性能或内存使用上有何不同?
迭代方法相比递归方法在管理内存上有更好的控制,因为它避免了递归调用栈的开销。在递归方法中,每次函数调用都会增加调用栈的深度,而对于深度很大的树来说,这可能导致栈溢出。迭代方法通过显式使用栈来处理节点,这样可以更直观地控制栈的大小,减少因深度大而造成的内存问题。此外,迭代方法通常在理解和调试方面更为直接,尤其是在复杂的树遍历操作中。
🦆
题解提到的进阶问题,如何优化算法以支持频繁的插入和删除操作?具体有哪些数据结构或技术可以用于优化?
为了优化算法以支持频繁的插入和删除操作,可以使用一些特殊的数据结构,如平衡二叉搜索树(例如AVL树或红黑树),这些树结构可以在插入和删除操作后自动保持平衡,从而保持操作的时间复杂度为O(log n)。此外,可以考虑使用Augmented Tree,其中节点增加了额外的信息,如子树的节点数量,这样可以更快地直接访问第k小的元素。还可以考虑使用Order Statistic Tree,它是一种可以直接通过节点的统计信息快速检索第k小元素的红黑树变种。
🦆
在迭代过程中,如果k大于树的节点总数会发生什么?算法是否有处理这种边界情况的机制?
如果k大于树的节点总数,则中序遍历完成后,k仍然大于0。在当前的算法实现中,这种情况会导致函数无返回值或可能引发错误,因为算法假设k是一个有效的输入。为了处理这种边界情况,可以在遍历开始前添加代码来检查树中节点的总数,如果节点总数小于k,则直接返回一个错误或特定的值,指示输入不合理。这需要额外的时间来计算树的大小,但可以防止无效操作。

相关问题

二叉树的中序遍历

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

 

示例 1:

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

示例 2:

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

示例 3:

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

 

提示:

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

 

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

二叉树中第二小的节点

给定一个非空特殊的二叉树,每个节点都是正数,并且每个节点的子节点数量只能为 2 或 0。如果一个节点有两个子节点的话,那么该节点的值等于两个子节点中较小的一个。

更正式地说,即 root.val = min(root.left.val, root.right.val) 总成立。

给出这样的一个二叉树,你需要输出所有节点中的 第二小的值

如果第二小的值不存在的话,输出 -1

 

示例 1:

输入:root = [2,2,5,null,null,5,7]
输出:5
解释:最小的值是 2 ,第二小的值是 5 。

示例 2:

输入:root = [2,2,2]
输出:-1
解释:最小的值是 2, 但是不存在第二小的值。

 

提示:

  • 树中节点数目在范围 [1, 25]
  • 1 <= Node.val <= 231 - 1
  • 对于树中每个节点 root.val == min(root.left.val, root.right.val)