题目描述
给定一个二叉搜索树的根节点 root
,和一个整数 k
,请你设计一个算法查找其中第 k
个最小元素(从 1 开始计数)。
样例
输入:root = [3,1,4,null,2], k = 1
输出:1
输入:root = [5,3,6,2,4,null,null,1], k = 3
输出:3
限制
- 树中的节点数为
n
。 1 <= k <= n <= 10^4
0 <= Node.val <= 10^4
进阶
- 如果二叉搜索树经常被修改(插入/删除操作)并且你需要频繁地查找第
k
小的值,你将如何优化算法?
算法
(中序遍历) $O(n)$
- 直接按照中序遍历规则遍历整棵二叉搜索树,并用数组记录结点的值。
- 返回第
k
节点的值。
时间复杂度
- 需要遍历整棵二叉搜索树,故时间复杂度为 $O(n)$。
空间复杂度
- 需要额外 $O(n)$ 的空间存储数组。
C++ 代码
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode() : val(0), left(nullptr), right(nullptr) {}
* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
* TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
* };
*/
class Solution {
private:
void solve(TreeNode* cur, vector<int>& vals) {
if (!cur)
return;
solve(cur -> left, vals);
vals.push_back(cur->val);
solve(cur->right, vals);
}
public:
int kthSmallest(TreeNode* root, int k) {
vector<int> vals;
solve(root, vals);
return vals[k - 1];
}
};
巨佬,这题的
提高版
怎么解啊?就是在包含频繁插入
和删除
操作的情况下。维护以每个节点为根的子树的大小 $sz_u$,通过递归可以找到第 $k$ 个节点。
插入的时候更新到根节点路径上 $sz$ 的值,删除比较麻烦,但可以通过左旋或者右旋方便的删除节点。
通常情况下,可以使用 splay 平衡树处理这类的问题。
强, 返回前k - 1个数就是第k个数了,不过空间上要大一点
这题目的follow up是不是给Node增加一个count属性,不知道有没有相应的题目?
我发现recursion虽然好理解,写起来代码也少,比较容易写,但是recursion似乎没有办法在任何时候想退出就退出,一旦写了recursion的解法,无论如何都要走完所有 case, 即便有时候并不需要走完所有case,只要找到解了就可以退出了,但是recursion不行, 一定会走完所有case,在traverse的过程中把结果存下来。
用bool类型的dfs可以停下来
为什么用bool类型的可以停下来呢
if (dfs(xx)) return true;
我是说如果中序遍历的写法是那种,走到第k个就可以跳出的话/
感觉不需要遍历整棵树啊,严格来说,应该是遍历到第k个数就完了。你这个是recusion的版本,我刚刚写了不是recursion 的版本,等下回来把recursion 的版本也写一下。