来自《剑指Offer》中的题,给定一棵二叉搜索树,请找出其中第k大的节点。
二叉树相关问题也是老面试问题了,想当年数据结构巅峰时期也是手撸AVL Tree,Splay Tree。现在一个前中后序遍历都要反应半天,改天还是要再系统地总结回顾一下二叉树相关的知识。
刚开始想的时候没反应过来二叉搜索树是个啥,接直接维护了个优先队列priority_queue,然后bfs把整个二叉树遍历了一遍。bfs遍历相比递归遍历的优点就是空间复杂度比较小。在递归遍历时,树是否平衡对空间复杂度的影响比较大(O(logN)~O(N))。
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode(int x) : val(x), left(NULL), right(NULL) {}
* };
*/
//用bfs,同时维护一个优先队列;
class Solution {
public:
int kthLargest(TreeNode* root, int k) {
priority_queue<int> leaves; //降序优先队列;
// priority_queue<int, vector<int>, greater<int> > 升序优先队列;
queue<TreeNode*> bfs;
bfs.push(root);
while (!bfs.empty()){
TreeNode* temp = bfs.front();
bfs.pop();
if (temp == NULL){
continue;
}
leaves.push(temp->val);
bfs.push(temp->left);
bfs.push(temp->right);
}
k--;
while (k--){
leaves.pop();
}
return leaves.top();
}
};
后来看了一下题解,意识到这是二叉搜索树。
二叉搜索树(Binary Search Tree):又叫二叉查找树、二叉排序树或有序二叉树。它或者是一颗空树,或者是具有下列性质的二叉树:若它的左子树不空,则左子树上所有结点的值均小于它的根结点的值;若它的右子树不空,则右子树上所有结点的值均大于它的根结点的值;它的左、右子树也分别为二叉排序树(左小右大)(百度百科)。
二叉搜索树中没有键值相等的节点。二叉搜索树的优势在于查找、插入的期望时间复杂度较低,都为O(logN),当树退化为单链表时最差,为O(n)。此外,对二叉搜索树进行中序遍历可以得到一个结点的有序序列,先遍历左子树则得到由小到大的序列,**先遍历右子树(即逆中序遍历)**则得到由大到小的序列。一个无序序列可以通过构造一颗二叉搜索树变成一个有序序列,构造树的过程即为对无序序列进行查找的过程。
每次插入的新的结点都是二叉搜索树上新的叶子结点,所以在进行插入操作时,不必移动其它结点。对二叉搜索树进行改进可以得到AVL树、红黑树等,从而将最坏效率降至O(logN)。
所以,在有了这个性质之后,很容易就可以想到直接逆中序遍历二叉搜索树,输出第K个结点的值就是第K大的值。
递归实现:
因为递归无法递归指定的层数后终止,所以必须设置两个全局变量来记录遍历到的结点的数量和保存结果。
class Solution {
//逆中序遍历二叉搜索树的递归写法;
public:
int kthLargest(TreeNode* root, int k){
inorder(root, k); //进行递归遍历;
return result;
}
void inorder(TreeNode* root, int k){
if (root == NULL){ //递归终止条件;
return;
}
if (root->right != NULL){ //逆中序,先右边;
inorder(root->right, k);
}
if (++counts == k){ //中间;
result = root->val;
}
if (root->left != NULL){ //左边;
inorder(root->left, k);
}
return;
}
int counts = 0; //当前遍历到的结点的个数;
int result = 0; //结果;
};
迭代实现:
class Solution {
/*逆中序遍历二叉搜索树的迭代写法,使用栈实现:
遇到一个结点入栈,然后遍历它的右子树,当右子树结束遍历后 ,从栈顶弹出
这个结点并访问,然后继续遍历它的左子树。
*/
public:
int kthLargest(TreeNode* root, int k){
stack<TreeNode*> inorder;
while (root != NULL || !inorder.empty()){
while (root != NULL){ //一直往右入栈;
inorder.push(root);
root = root->right;
}
if (!inorder.empty()){
k--;
if (k == 0){
return inorder.top()->val;
}
root = inorder.top();
inorder.pop();
root = root->left; //开始往左遍历;
}
}
return 0;
}
};