题目回顾
给定一棵二叉搜索树(BST)的根节点 root 和一个整数 k,返回这棵树中第 k 小的节点值(1-indexed)。这道题是 LeetCode 230「Kth Smallest Element in a BST」,也经常出现在面试中。algo+1
因为是 BST,它满足「左子树所有值 < 根节点 < 右子树所有值」的性质,这一点是设计解法的关键。takeuforward
初始误区:preorder 能不能做?
一个很自然的想法是:「随便用一种遍历方式(比如 preorder),在遍历过程中数到第 k 个节点时返回它。」
但是在 BST 里,只有 inorder 遍历(左--根--右)才能保证访问顺序是从小到大;preorder(根--左--右)得到的序列一般不是有序的,所以用 preorder 数到的第 k 个节点并不等于第 k 小的节点。这是第一个需要修正的点。heycoach+1
总结一下:
- BST + inorder → 访问顺序是按值从小到大。
- 想要「第 k 小」,就要在 inorder 过程中数第 k 次访问到的节点,而不是随便选一种遍历。algo+1
正确核心思路:递归 inorder + 外部计数
利用 inorder 的有序性,一个直接的解法是:递归做 inorder 遍历,同时维护一个外部计数器 count,当访问到第 k 个节点时记录答案并提前结束遍历。finalroundai+1
伪代码思路大致如下(语言无关):
-
准备外部变量:
count:已经访问过的节点数,初始为 0ans:答案found:是否已经找到第 k 小,初始为 false
-
定义递归函数
inorder(node):- 如果
node为null或者found已经为true,直接返回(提前剪枝)。 - 递归访问左子树。
- 访问当前节点时:
count++;如果count == k,则记录ans = node.val,found = true并return。 - 递归访问右子树。
- 如果
这种方式的关键点:
- 正确的计数时机: 在「访问当前节点」时
count++(即左子树处理完之后)。 - 注意 off-by-one: 题目给的是 1-indexed,第 1 小就是第一次访问到的节点,所以条件是
count == k,而不是k+1。finalroundai+1
时间复杂度和空间复杂度:
- 最坏情况下 ,要走到第 k 个节点,时间复杂度是 O(h + k) ,h 为树高,最坏 O(n);因为一旦找到第 k 个就可以停止,不必遍历整棵树。heycoach+1
- 使用递归,递归栈的深度与树高 h 成正比,所以空间复杂度 是 O(h) ,最坏也是 O(n)。algomaster+1
**如果你更喜欢迭代写法,**可以用显式栈模拟 inorder,复杂度同样是 O(h + k) 时间、O(h) 额外空间。codecraftbyprasad+1
Follow-up:频繁修改时怎么优化?
Follow-up 问的是更偏「数据结构设计」的问题:如果这棵 BST 经常执行插入、删除,并且频繁查询第 k 小,用每次 inorder 从头数的方式就不够高效了,因为每次查询仍然可能接近 O(n)。stackoverflow+1
更好的思路是「给 BST 做扩展(augment)」:
-
在每个节点上增加一个字段
size,表示「以该节点为根的子树一共有多少个节点」。 -
维护规则:
- 插入、删除节点时,沿路径回溯更新 size(和普通 BST 维护高度一样的思路)。
- 这样任意节点的
size都可以在 O(1) 时间读到。
-
**查询第 k 小的流程(类似 order statistic tree):**wikipedia+2
- 记当前节点为
x,左子树大小为L = size(x.left)(如果为空则为 0)。 - 如果
k == L + 1,说明当前节点就是第 k 小,直接返回x。 - 如果
k ≤ L,说明第 k 小在左子树里,递归/迭代到左子树继续找。 - 如果
k > L + 1,说明第 k 小在右子树里,转到右子树,同时寻找的是「右子树中的第 (k − L − 1) 小」。
- 记当前节点为
在一棵高度为 h 的树上,这样的查询只沿着从根到叶的一条路径走,所以查询时间是 O(h) ;如果再配合自平衡树(如 AVL、红黑树),就可以保证 h 为 O(log n) ,从而增删查(包括第 k 小)都是 O(log n)。heycoach+2
小结
这道题的关键不在于「怎么写遍历」,而在于「如何利用 BST 的有序结构」:
- 基础版: 用 inorder + 外部计数/参数计数,在访问到第 k 个节点时返回,时间 O(h + k),空间 O(h)。algo+1
- 进阶版: 通过在每个节点存子树大小,把普通 BST 扩展成 order statistic tree,使查询第 k 小可以在 O(h) 时间完成,并且在自平衡的前提下稳定达到 O(log n)。stackoverflow+1