跳到正文
第 230 题中等

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

Kth Smallest Element in a BST

返回二叉搜索树中第 k 小的节点值(k 从 1 开始)。

在力扣中练习

题目描述

给定一个二叉搜索树的根节点 root ,和一个整数 k ,请你设计一个算法查找其中第 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 小的值,你将如何优化算法?

解题思路

主解法

中序迭代 · 提前停止

BST 中序升序,数到第 k 个就停

BST 的中序遍历是升序序列,所以“第 k 小”就是中序访问到的第 k 个节点。不需要把所有节点存下来。

沿用第 94 题的迭代中序模板:沿左链压栈,弹出即访问,然后转向右子树。每弹出一个节点就把计数加一,等于 k 时立刻返回。

分步图解

k=5;中序遍历到第 5 个节点就停,不需要遍历完整棵树。

中序遍历 · 绿色是已出栈的节点左子树 · 右子树
831016144713
栈 · 保存尚未访问根的祖先后进先出
从栈顶进入与离开
    空栈
    栈底
    第 k 小 · k=5
    已访问 0
    1 / 4
    中序每弹出一个节点就计数;第 1 小是 1,第 2 小是 3。

    自拟树有 9 个节点,k=5。前四个是 1、3、4、6,第五个是 7;找到后右半边 10、13、14 都不会再访问。

    分步图解

    沿左链压栈 6 → 4;最小的未访问节点在栈顶。

    中序遍历 · 绿色是已出栈的节点左子树 · 右子树
    831016144713
    栈 · 保存尚未访问根的祖先后进先出
    从栈顶进入与离开
    1. 8
    2. 6
    3. 4← top
    栈底
    第 k 小 · k=5
    已访问 2
    1 / 4
    继续弹出 4、6、7;计数到 k 时立即返回,不访问右半边。

    时间 O(h + k):最多压入 h 个祖先,再访问 k 个节点;空间 O(h)。如果每次插入/删除后频繁查询第 k 小,可以改用在节点里维护子树大小,做到 O(h)。

    时间 O(h + k)空间 O(h)

    易错点

    • 用前序或后序计数:得到的不是升序序列。
    • 找到后继续遍历:结果正确但多做无用功,也可能覆盖答案。
    • 计数器从 0 开始却用 count == k 在弹出前判断:会错过第 k 个。
    对照解法

    中序递归 · 全局计数

    递归中序,数到 k 就记录答案

    逻辑同样简洁,但递归深度为 h,且提前返回要靠额外标记;迭代版更容易在 k 处停。

    递归中序的访问顺序天然升序:先递归左子树,再访问当前节点,最后递归右子树。

    用一个成员计数器和一个答案变量。访问节点时先自增,若等于 k 就记录答案并返回;之后的递归通过 ans != -1 提前结束。

    它的可读性很好,但需要额外状态来终止递归;如果树退化成链,递归深度为 n。

    1. 1递归左子树。
    2. 2访问当前节点,计数器加一。
    3. 3等于 k 时记录答案并终止。
    4. 4递归右子树。
    时间 O(h + k)空间 O(h)

    易错点

    • 找到答案后没有终止标记:仍会继续遍历整棵树。
    • 先访问再递归左子树:顺序变成根-左-右,不再是升序。

    代码与演示

    主解法配有图解;对照解法用于比较思路、复杂度或实现方式。

    演示输入root = [8,3,10,1,6,null,14,null,null,4,7,13],k = 5

    k=5;中序遍历到第 5 个节点就停,不需要遍历完整棵树。

    中序遍历 · 绿色是已出栈的节点左子树 · 右子树
    831016144713
    栈 · 保存尚未访问根的祖先后进先出
    从栈顶进入与离开
      空栈
      栈底
      第 k 小 · k=5
      已访问 0
      1 / 6
      查看 C++ 代码

      延伸阅读

      • BST 的中序序列就是排序结果,很多“第 k 小/第 k 大”都可以套这个模板。
      • 第 k 大可以改成“右-根-左”的逆中序,或者先求总节点数再转换。