LeetCode 230-Kth Smallest Element in a BST
题干:
给定一棵二叉搜索树,求它的第k大元素的值是多少。
解:
方法1:
利用中序遍历得到有序数组的特性。
int cnt = 0;
int ans;
int kthSmallest(TreeNode* root, int k) {
traverse(root, k);
return ans;
}
void traverse(TreeNode* root, int k){
if(!root) return;
traverse(root->left, k);
++cnt;
if(cnt == k){
ans = root->val;
return;
}
else if(cnt > k) return;
traverse(root->right, k);
}
方法2(优化):
利用节点所在的子树大小,得到节点排名,从而判断第 k k k大元素在哪棵子树中(或为根节点)。
siz[x]
数组记录以
x
x
x为根节点的子树的大小。在一棵子树中,根节点的排名取决于左子树的大小,所以若要寻找第
k
k
k大元素,需要将
k
k
k与左子树大小siz[x->left]
作对比,若:
- s i z ≥ k siz\ge k siz≥k,说明第 k k k大元素在左子树中,往左子树递归;
- s i z = = k − 1 siz == k-1 siz==k−1,说明根节点就是第 k k k大元素,返回根节点的值;
- s i z < k − 1 siz < k - 1 siz<k−1,说明第 k k k大元素在右子树中,往右子树递归
(注意,此题不能自己额外定义BST的size字段,在增删元素时也不能动态维护,所以在求siz时需要额外遍历整棵树,复杂度并未优化)
map<TreeNode*, int> siz;
int kthSmallest(TreeNode* root, int k) {
getSize(root);
return queryKth(root, k);
}
void getSize(TreeNode* root){
if(!root){
siz[root] = 0;
return;
}
getSize(root->left);
getSize(root->right);
siz[root] = siz[root->left] + siz[root->right] + 1;
}
int queryKth(TreeNode* root, int k){
if(siz[root->left] >= k) return queryKth(root->left, k);
else if(siz[root->left] < k - 1) return queryKth(root->right, k - siz[root->left] - 1);
else return root->val;
}
若初始时定义了size字段(或额外siz数组)+增删元素时能够动态维护size,那么该算法的时间复杂度为 O ( l o g n ) O(logn) O(logn)
struct TreeNode {
int val;
int size; // 记录以该节点为根节点的子树的大小
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) {}
};