在问 · 算法题解

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

中等二叉树迭代中序遍历力扣原题 ↗

题目描述

输入一棵二叉搜索树的根节点 root 和一个整数 k。树用层序数组表示,空位记为 null。

把树中所有节点值从小到大排列,返回排在第 k 位的值。k 从 1 开始数。

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

解释:升序排列为 1,2,3,4,第 1 个是 1。

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

解释:升序排列为 1,2,3,4,5,6,第 3 个是 3。

数据范围

  • 树的节点总数记为 n。
  • 1 <= k <= n <= 10^4
  • 0 <= Node.val <= 10^4

进阶:如果这棵树会被频繁插入和删除节点,同时又要频繁查询第 k 小的值,应该怎样改进?

解题思路:迭代中序遍历

执行迭代中序遍历,每弹出一个节点就将 k 减一,减到零时返回。

为什么正确

中序已访问节点严格按升序排列,k 表示还需访问多少个节点。

复杂度分析

最坏时间 O(n),提前停止时 O(h+k),空间 O(h)。

易错点

k 从 1 开始计数;在弹栈访问时减一,而不是入栈时。

参考代码

Python、Java、C++、Go、JavaScript、TypeScript 六种语言,逻辑一致。

class Solution:
    def kthSmallest(self, root, k):
        stack = []
        cur = root
        while cur or stack:
            while cur:
                stack.append(cur)
                cur = cur.left
            cur = stack.pop()
            k -= 1
            if k == 0:
                return cur.val
            cur = cur.right

载入编辑器,自己跑一遍 →

其他解法

迭代中序遍历可以提前停止,最常用;子树节点计数把预处理和查询分开,适合多次查询;Morris 遍历用临时线索换来 O(1) 空间。

解法 2:子树节点计数 时间 O(n),空间 O(n)

先递归统计以每个节点为根的子树节点数,存入哈希表。从根出发,设左子树有 left 个节点:k ≤ left 时去左子树;k = left + 1 时当前节点就是答案;否则令 k 减去 left + 1 后去右子树。

单次查询要先花 O(n) 统计。统计结果可以复用,之后每次查询只需 O(h),适合频繁查询第 k 小的进阶场景。

class Solution:
    def kthSmallest(self, root, k):
        size = {}

        def count(node):
            if not node:
                return 0
            size[node] = 1 + count(node.left) + count(node.right)
            return size[node]

        count(root)
        node = root
        while node:
            left = size.get(node.left, 0) if node.left else 0
            if k <= left:
                node = node.left
            elif k == left + 1:
                return node.val
            else:
                k -= left + 1
                node = node.right
        return -1

解法 3:Morris 中序遍历 时间 O(n),空间 O(1)

当前节点有左子树时,找到左子树的最右节点。最右节点的右指针为空就让它指向当前节点,然后进入左子树;不为空说明左子树已访问完,恢复指针、访问当前节点、进入右子树。没有左子树时直接访问当前节点并进入右子树。每访问一个节点 k 减一,减到零时记录答案。

不用栈,额外空间 O(1)。遍历中会临时修改树指针,为了恢复树结构要走完整棵树,代码也更难写对。

class Solution:
    def kthSmallest(self, root, k):
        ans = -1
        cur = root
        while cur:
            if cur.left:
                pre = cur.left
                while pre.right and pre.right is not cur:
                    pre = pre.right
                if pre.right is None:
                    pre.right = cur
                    cur = cur.left
                    continue
                pre.right = None
            k -= 1
            if k == 0:
                ans = cur.val
            cur = cur.right
        return ans

二叉树 专题的其他题目

查看 二叉树 全部题目 →