zoukankan      html  css  js  c++  java
  • 【LeetCode & 剑指offer刷题】树题16:Kth Smallest Element in a BST

    【LeetCode & 剑指offer 刷题笔记】目录(持续更新中...)

    Kth Smallest Element in a BST

    Given a binary search tree, write a function kthSmallest to find the kth smallest element in it.
    Note: 
    You may assume k is always valid, 1 ≤ k ≤ BST's total elements.
    Example 1:
    Input: root = [3,1,4,null,2], k = 1
      3
     /
    1   4
     
      2
    Output: 1
    Example 2:
    Input: root = [5,3,6,2,4,null,null,1], k = 3
            5
           /
          3   6
         /
        2   4
       /
      1
    Output: 3
    Follow up:
    What if the BST is modified (insert/delete operations) often and you need to find the kth smallest frequently? How would you optimize the kthSmallest routine?

    C++
     
    /**
     * Definition for a binary tree node.
     * struct TreeNode {
     *     int val;
     *     TreeNode *left;
     *     TreeNode *right;
     *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
     * };
     */
     
    /*
    方法一:中序遍历递归法
    BST中序遍历之后为从小到大排列
    中序遍历递归法,不是最优的,因为是遍历完之后在给出的结果(可用迭代法进行改进,参考题目 Validate Binary Search Tree
    注:可在递归中加入判断进行减枝
    */
    class Solution
    {
    public:
        int kthSmallest(TreeNode* root, int k)
        {
            vector<int> nodes;
            inorder(root, nodes);
            return nodes[k-1];
        }
       
        void inorder(TreeNode* root, vector<int>& nodes)
        {
            if(root == nullptr) return; //递归的出口
           
            inorder(root->left, nodes);
            nodes.push_back(root->val);
            inorder(root->right, nodes);
        }
    };
     
    /*
    方法二:中序遍历迭代法
    在遍历的过程中统计数量
    */
    class Solution
    {
    public:
        int kthSmallest(TreeNode* root, int k)
        {
            int cnt = 0;
            stack<TreeNode*> s;
            TreeNode *p = root;
            while (!s.empty() || p)
            {
                if(p) //左结点不为空时
                {
                    s.push(p);   //入栈
     
                    p = p->left; //指向下一个左结点
                }
                else //左结点为空时
                {
                    p = s.top();
                    cnt++; //统计数目(遍历到了要访问的父结点)
                    if (cnt == k) return p->val;
                    s.pop();
                   
                    p = p->right;  //指向右结点             
                }
              
            }
            return 0;
        }
    };
    /*
    方法三:分治法
    首先计算出左子树的结点个数总和cnt
    如果k小于等于左子树结点总和cnt,说明第k小的元素在左子树中,直接对左子结点调用递归即可。
    如果k大于cnt+1,说明目标值在右子树中,对右子结点调用递归函数
    如果k等于cnt+1,则当前结点即为所求
    */
    class Solution
    {
    public:
        int kthSmallest(TreeNode* root, int k)
        {
            int cnt = count(root->left);
            if (k <= cnt)
            {
                return kthSmallest(root->left, k);
            }
            else if (k > cnt + 1)
            {
                return kthSmallest(root->right, k - cnt - 1); //注意此处变为k - (cnt-1)
            }
            else
                return root->val;
        }
        int count(TreeNode* node)
        {
            if (!node) return 0;
            return 1 + count(node->left) + count(node->right);
        }
    };
    /*
    Follow up: 假设该BST被修改的很频繁,而且查找第k小元素的操作也很频繁,问我们如何优化
    方法:改进方法三
    修改原树结点的结构,使其保存包括当前结点和其左右子树所有结点的个数,
    这样我们使用的时候就可以快速得到任何左子树结点总数来帮我们快速定位目标值了
     
    分析:
    对于查找很频繁的情况,由于保存了结点个数,每次查找时,不需要递归统计结点个数,这样可以节省大量时间
    对于修改很频繁的情况,插入或者删除某个结点时,需更新其所有祖先结点,所以该方法并不太适用于修改很频繁的情况
    */
     
    class Solution
    {
    private:
        struct MyTreeNode
        {
            int val;
            int count;
            MyTreeNode *left;
            MyTreeNode *right;
            MyTreeNode(int x) : val(x), count(1), left(NULL), right(NULL) {}
        };
        
    public:
        int kthSmallest(TreeNode* root, int k)
        {
            MyTreeNode *node = build(root);
            return helper(node, k);
        }
        
        MyTreeNode* build(TreeNode* root)
        {
            if (!root)
                return NULL;
            else
            {
                MyTreeNode *node = new MyTreeNode(root->val); //count在构造函数中被初始化为1
                node->left = build(root->left);
                node->right = build(root->right);       
                if (node->left) node->count += node->left->count; //统计数量
                if (node->right) node->count += node->right->count;     
                
                return node;
            }  
        }
       
        int helper(MyTreeNode* node, int k)
        {
            if (node->left)
            {
                int cnt = node->left->count; //左结点存储了当前结点左子树的所有结点个数
                if (k <= cnt)
                    return helper(node->left, k);
                else if (k > cnt + 1)
                    return helper(node->right, k - 1 - cnt);
                else
                    return node->val;
            }
            else
            {
                if (k == 1)
                    return node->val;
                else
                    return helper(node->right, k - 1);
               
            }
        }
    };
     
     
  • 相关阅读:
    vue.js环境配置步骤及npm run dev报错解决方案
    消息处理之performSelector
    iOS开发网络篇—发送GET和POST请求(使用NSURLSession)
    iOS手机号正则表达式并实现344格式 (正则的另一种实现方式)
    IOS开发——正则表达式验证手机号、密码
    iOS网络请求之---GET和POST
    CLLocation
    ios本地文件内容读取,.json .plist 文件读写
    UITableViewCell的4种样式
    iOS开发通过代码方式使用AutoLayout (NSLayoutConstraint + Masonry)
  • 原文地址:https://www.cnblogs.com/wikiwen/p/10225851.html
Copyright © 2011-2022 走看看