zoukankan      html  css  js  c++  java
  • 算法-二分查找与二叉排序树

    示例:给定一个 n 个元素有序的(升序)整型数组 nums 和一个目标值 target  ,写一个函数搜索 nums 中的 target,如果目标值存在返回下标,否则返回 -1。


    示例 1:

    输入: nums = [-1,0,3,5,9,12], target = 9
    输出: 4
    解释: 9 出现在 nums 中并且下标为 4

    示例 2:

    输入: nums = [-1,0,3,5,9,12], target = 2
    输出: -1
    解释: 2 不存在 nums 中因此返回 -1

     

    提示:

        你可以假设 nums 中的所有元素是不重复的。
        n 将在 [1, 10000]之间。
        nums 的每个元素都将在 [-9999, 9999]之间。

    来源:力扣(LeetCode)
    链接:https://leetcode-cn.com/problems/binary-search
    著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

    class Solution {
    public:
        int search(vector<int>& nums, int target) {
            int begin=0,end=nums.size()-1;
            int mid;
            while(begin<=end){
                mid=(begin+end)/2;
                if(target==nums[mid]) return mid;
                else if(target<nums[mid]){
                    end=mid-1;
                }else{
                    begin=mid+1;
                }
            }
            return -1;
        }
    };

    1.搜索插入位置

    给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引。如果目标值不存在于数组中,返回它将会被按顺序插入的位置。

    你可以假设数组中无重复元素。

    示例 1:

    输入: [1,3,5,6], 5
    输出: 2

    示例 2:

    输入: [1,3,5,6], 2
    输出: 1

    示例 3:

    输入: [1,3,5,6], 7
    输出: 4

    示例 4:

    输入: [1,3,5,6], 0
    输出: 0

    来源:力扣(LeetCode)
    链接:https://leetcode-cn.com/problems/search-insert-position
    著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

    class Solution {
    public:
        int searchInsert(vector<int>& nums, int target) {
            int begin=0,end=nums.size()-1;
            int mid;
            while(begin<=end){
                mid=(begin+end)/2;
                if(target==nums[mid])
                    return mid;
                else if(target<nums[mid]){
                    end=mid-1;
                }else{
                    begin=mid+1;
                }
            }
            if(target<nums[mid]) return mid;
            else return mid+1;
        }
    };

     

    2. 在排序数组中查找元素的第一个和最后一个位置

    给定一个按照升序排列的整数数组 nums,和一个目标值 target。找出给定目标值在数组中的开始位置和结束位置。

    你的算法时间复杂度必须是 O(log n) 级别。

    如果数组中不存在目标值,返回 [-1, -1]。

    示例 1:

    输入: nums = [5,7,7,8,8,10], target = 8
    输出: [3,4]

    示例 2:

    输入: nums = [5,7,7,8,8,10], target = 6
    输出: [-1,-1]

    来源:力扣(LeetCode)
    链接:https://leetcode-cn.com/problems/find-first-and-last-position-of-element-in-sorted-array
    著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

    class Solution {
    public:
        vector<int> searchRange(vector<int>& nums, int target) {
            int begin=0,end=nums.size()-1;
            int mid;
            vector<int> pos(2,-1);
            pos[0]=bound(nums,target,0);
            pos[1]=bound(nums,target,1);
            return pos;
                
        }
        int bound(vector<int>& nums,int target,int b){
            int begin=0,end=nums.size()-1;
            int mid;
            while(begin<=end){
                mid=(begin+end)/2;
                if(target==nums[mid]){
                    if(b==0){
                        if(mid==0 || nums[mid-1]<target){
                            return mid;
                        } 
                        end=mid-1;
                    }
                    if(b==1){
                        if(mid==nums.size()-1 || nums[mid+1]>target){
                            return mid;
                        }
                        begin=mid+1;
                    }
                }else if(target<nums[mid]){
                    end=mid-1;
                }else{
                    begin=mid+1;
                }
            }
            return -1;
        }
    };

    3.搜索旋转排序数组

    假设按照升序排序的数组在预先未知的某个点上进行了旋转。

    ( 例如,数组 [0,1,2,4,5,6,7] 可能变为 [4,5,6,7,0,1,2] )。

    搜索一个给定的目标值,如果数组中存在这个目标值,则返回它的索引,否则返回 -1 。

    你可以假设数组中不存在重复的元素。

    你的算法时间复杂度必须是 O(log n) 级别。

    示例 1:

    输入: nums = [4,5,6,7,0,1,2], target = 0
    输出: 4

    示例 2:

    输入: nums = [4,5,6,7,0,1,2], target = 3
    输出: -1

    来源:力扣(LeetCode)
    链接:https://leetcode-cn.com/problems/search-in-rotated-sorted-array
    著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

    class Solution {
    public:
        int search(vector<int>& nums, int target) {
            int begin=0,end=nums.size()-1,mid;
            while(begin<=end){
                mid=(begin+end)/2;
                if(target==nums[mid]){
                    return mid;
                }else{
                    if(nums[mid]>=nums[begin]){
                        if(target<nums[begin] || target>nums[mid]){
                            begin=mid+1;
                        }else{
                            end=mid-1;
                        }
                    }else{
                        if(target>nums[end] || target<nums[mid]){
                            end=mid-1;
                        }else{
                            begin=mid+1;
                        }
                    }
                }               
            }
            return -1;
        }
    };

    4.最长上升子序列

    给定一个无序的整数数组,找到其中最长上升子序列的长度(严格上升,不必连续)。

    示例:

    输入: [10,9,2,5,3,7,101,18]
    输出: 4 
    解释: 最长的上升子序列是 [2,3,7,101],它的长度是 4

    说明:

    • 可能会有多种最长上升子序列的组合,你只需要输出对应的长度即可。
    • 你算法的时间复杂度应该为 O(n2) 。

    进阶: 你能将算法的时间复杂度降低到 O(n log n) 吗?

    //寻找第一个大于x的元素下标,返回start.
    int binary_search(int x, vector<int>& s) {
        int start = 0;
        int end = s.size() - 1;
        int mid;
        while (start<=end) {
            mid = (start + end) / 2;
            if (s[mid] > x) {
                end = mid - 1;
            } else if (s[mid] < x) {
                start = mid + 1;
            } else {
                return mid;
            }
        }
        return start;
    }
        
    
    class Solution{
    public:
        int lengthOfLIS(vector<int>& nums) {
            int len=nums.size();
            if(len==0) return 0;
            vector<int> stack;
            stack.push_back(nums[0]);
            int index=-1;
            for(int i=1;i<len;i++){
                if(nums[i]>stack.back()){
                    stack.push_back(nums[i]);
                }else{
                    index=binary_search(nums[i],stack);
                    stack[index]=nums[i];
                }
            }
            return stack.size();
        }
    };

     若是寻找第一个小于x的元素下标,则返回end


     5.序列化和反序列化二叉搜索树

    序列化是将数据结构或对象转换为一系列位的过程,以便它可以存储在文件或内存缓冲区中,或通过网络连接链路传输,以便稍后在同一个或另一个计算机环境中重建。

    设计一个算法来序列化和反序列化二叉搜索树。 对序列化/反序列化算法的工作方式没有限制。 您只需确保二叉搜索树可以序列化为字符串,并且可以将该字符串反序列化为最初的二叉搜索树。

    编码的字符串应尽可能紧凑。

    注意:不要使用类成员/全局/静态变量来存储状态。 你的序列化和反序列化算法应该是无状态的。

    来源:力扣(LeetCode)
    链接:https://leetcode-cn.com/problems/serialize-and-deserialize-bst
    著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

    /**
     * Definition for a binary tree node.
     * struct TreeNode {
     *     int val;
     *     TreeNode *left;
     *     TreeNode *right;
     *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
     * };
     */
    class Codec {
    public:
    
        // Encodes a tree to a single string.
        string serialize(TreeNode* root) {
            string data="";
            preorder(root,data);
            return data;
        }
        void preorder(TreeNode* root,string& data){
            if(!root) return;  
            data+=intToString(root->val);
            data+='#';
            preorder(root->left,data);
            preorder(root->right,data);
        }
        //整数转化为字符串
        string intToString(int val) {
            string s = "";
            while (val) {
                s += val % 10 + '0';
                val /= 10;
            }    
            string res="";
            //需要倒序得到正确字符串
            for(int i=s.size()-1;i>=0;i--){
                res+=s[i];
            }
            return res;
        }
        // Decodes your encoded data to tree.
        TreeNode* deserialize(string data) {
            TreeNode* root=NULL;
            int tmp=0;
            for(int i=0;i<data.size();i++){ 
                while(data[i]!='#'){
                    tmp=tmp*10+data[i]-'0';
                    i++;
                }            
                TreeNode* node=new TreeNode(tmp);
                if(root==NULL) root=node;
                else{
                   insert(root,node); 
                }            
                tmp=0;
            }
            return root;
            
        }
        //逐个插入构成二叉排序树
        void insert(TreeNode* root,TreeNode* node){      
            if(node->val<root->val){
                if(root->left==nullptr){
                    root->left=node;
                    
                }else{
                    insert(root->left,node);
                }
            }else{
                if(root->right==nullptr){
                    root->right=node;
                }else{
                    insert(root->right,node);
                }
            }
        }
    };
    
    // Your Codec object will be instantiated and called as such:
    // Codec codec;
    // codec.deserialize(codec.serialize(root));

    6. 计算右侧小于当前元素的个数 (逆序数)

    给定一个整数数组 nums,按要求返回一个新数组 counts。数组 counts 有该性质: counts[i] 的值是  nums[i] 右侧小于 nums[i] 的元素的数量。

    示例:

    输入: [5,2,6,1]
    输出: [2,1,1,0]
    解释:
    5 的右侧有 2 个更小的元素 (2 和 1).
    2 的右侧仅有 1 个更小的元素 (1).
    6 的右侧有 1 个更小的元素 (1).
    1 的右侧有 0 个更小的元素.

    来源:力扣(LeetCode)
    链接:https://leetcode-cn.com/problems/count-of-smaller-numbers-after-self
    著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

    class Solution {  
        struct TreeNode{
            int val;
            int count;
            TreeNode* left;
            TreeNode* right;
            TreeNode(int x,int c):val(x),count(c),left(NULL),right(NULL){}
        };
    public:
        vector<int> countSmaller(vector<int>& nums) {
            TreeNode* root=NULL;
            vector<TreeNode*> node_vec;
            vector<int> tmp_res;
            for(int i=nums.size()-1;i>=0;i--){
                int count_smaller=0;
                TreeNode *node=new TreeNode(nums[i],0);  
                node_vec.push_back(node);
                if(root==nullptr) {
                    root=node;                
                }
                else {
                    insert(root,node,count_smaller);            
                }
                tmp_res.push_back(count_smaller);
            }
            vector<int> result;
            for(int i=nums.size()-1;i>=0;i--){
                delete node_vec[i];
                result.push_back(tmp_res[i]);
            }  
            return result;
        }
        void insert(TreeNode* root,TreeNode* node,int & count_smaller){
            if(node->val<=root->val){
                root->count++;//**********记录左子树节点数目*************
                if(root->left){
                    insert(root->left,node,count_smaller);
                }else{
                    root->left=node;       
                }   
            }else{
                count_smaller+=root->count+1;
                if(root->right){
                    insert(root->right,node,count_smaller);
                }else{
                    root->right=node;
                }
            }       
        }
    };

    倒序插入二叉排序树,保证当前插入值的右侧已经在树中

    root->count记录左子树数目,即小于节点值的节点数目。这样当前值小于节点,root->count++;当前值大于节点,就要加上节点的root->count+1.如此一直到应该插入时,就累加得到了count_smaller的值

  • 相关阅读:
    .NET 4.5 异步IO
    使用MANIFEST.MF文件来track War包做持续部署
    .NET 4.5 压缩
    自定义实现URL重写 04.18
    对.Net Framework的认识
    那点所谓的分布式——memcache
    不能选择FreeTextBox下拉列表框
    实战架构设计
    LoadRunner压力测试心得总结
    JavaScript的模块化:封装(闭包),继承(原型)
  • 原文地址:https://www.cnblogs.com/chendaniu/p/10743108.html
Copyright © 2011-2022 走看看