二叉搜索树的第K大节点

来自《剑指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; 
    }
};
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值