zoukankan      html  css  js  c++  java
  • 在排序数组中查找数字

    第一题,数字在排序数组中出现的次数

      统计一个数字在排序数组中出现的次数. 例如输入排序数组{1,2,3,3,3,3,4,5},由于3在这个数中出现了4次,输出4.

    思路

      如何利用二分查找找到第一个k? 二分查找算法总是先拿数组中间的数组和k作比较.

    1. 如果中间数字比k大,那么k只可能出现数组的前半段,下一轮只在数组的前半段查找就可以了.
    2. 如果中间数字比k小,那么k只可能出现数组的后半段,下一轮只在数组的后半段查找就可以了.
    3. 如果中间数组和k相等,先判断这个数字是不是第一个k.
    4. 如果位于中间数字前面的一个数字不是k,那么此时中间的数字刚好就是第一个k.
    5. 如果中间数字的前面一个数字也是k,也就是说第一个k肯定在数组的前半段,下一轮仍然需要在数组的前半段查找.

      基于同样的思路可以在排序数组中找到最后一个k.

    1. 如果中间数字比k大,那么k只能出现在数组的前半段.
    2. 如果中间数字比k小,那么k只能出现在数组的后半段.
    3. 如果中间数子和k相等,需要判断这个数字是不是最后一个k.
    4. 如果位于中间数字后面一个数字不是k,那么此时中间的数字刚好就是最后一个k.
    5. 如果中间数字的后面一个数字也是k,也就是说第一个k肯定在数组的后半段,下一轮仍然需要在数组的后半段查找.
    #include <iostream>
    #include <vector>
    using namespace std;
    
    class Solution
    {
        public:
            int get_num_k(const vector<int> &v,int k);
            int get_first_k(const vector<int> &v,int k,int start,int end);
            int get_last_k(const vector<int> &v,int k,int start,int end);
    };
    int Solution::get_num_k(const vector<int> &v,int k)
    {
        if(v.empty()||v.size()<0)
            return -1;
        
        int first=get_first_k(v,k,0,v.size()-1);
        int last=get_last_k(v,k,0,v.size()-1);
        if(first>=0&&last<v.size()  ) 
            return last-first+1;
        else
            return 0;    
    }
    int Solution::get_first_k(const vector<int> &v,int k,int start,int end)
    {
        if(v.empty()||v.size()<0)
            return -1;
            
        int mid=(start+end)>>1;
        int mid_value=v[mid];
        if(k==mid_value)
        {
            if((mid>0&&v[mid-1]!=k)||mid==0)
                return mid;
            else
                end=mid-1;
        }
        else if(k>mid_value)
            start=mid+1;
        else
            end=mid-1;
            
        return get_first_k(v,k,start,end);
    }
    int Solution::get_last_k(const vector<int> &v,int k,int start,int end)
    {
        if(v.empty()||v.size()<0)
            return -1;
        
        int mid=(start+end)/2;
        int mid_value=v[mid];
        if(mid_value==k)
        {
            if((mid<v.size()-1&&v[mid+1]!=k)||mid==v.size()-1)
                return mid;
            else
                start=mid+1;
        }
        else if(mid_value>k)
            end=mid-1;
        else
            start=mid+1;
            
        return get_last_k(v,k,start,end);
    }
    int main()
    {
        vector<int> v{1,2,3,3,3,3,4,5};
        int k;
        cin>>k;
        
        Solution s;
        cout<<s.get_num_k(v,k)<<endl;
        return 0;
    }

    第二题,0~n-1中缺失的数字

      一个长度为n-1的递增排序数组中的所有数字都是唯一的,并且每个数字都在范围0~n-1之内。在范围0 ~ n-1内的n个数字中有且只有一个数字不在该数组中,请找出这个数字。

    思路

      1.先用公式n*(n-1)/2求出0~n-1所有数字之和s1,在求出数组所有数字之和s2,那个不在数组中的数字就是s1-s2的差,这种解法需要O(n)的时间

      2.数组一开始的一些数字与他们的下表相同,如果说不在数组中的那个数组记为m,那么所有比m小的数字下标与他们的值都相同。可以基于二分查找

        1>如果中间元素的值和它的下标相等,那么下一轮只需要找它的右半边

        2>如果中间元素的值和它的下标不等,并且它前一个元素和它的下标相等,意味着这个中间的数字就是第一个值与下标不等的数字。

        3>如果中间元素的值和它的下标不等,并且他前面的元素和它的下标相等不等,那么只需查找左半边。

    #include <iostream>
    #include <vector>
    using namespace std; 
    
    class Solution
    {
        public:
            int get_miss_num(const vector<int> &v,int len);
    };
    int Solution::get_miss_num(const vector<int> &v,int len)
    {
        if(v.empty()||v.size()<=0||len<=0)
            return -1;
        
        int left=0,right=len-1;
        while(left<=right)
        {
            int mid=(left+right)>>1;
            if(v[mid]!=mid)
            {
                if(mid==0||v[mid-1]==mid-1)
                    return mid;
                else
                    right=mid-1;
            }
            else
                left=mid+1;
        }
        if(left==len)
            return len;
    }
    int main()
    {
        vector<int> v{1};
        Solution s;
        cout<<s.get_miss_num(v,1)<<endl;
        return 0;
    }

    第三题,数组中数值和它的下标相等的元素

      一个长度为n-1的递增排序数组中的所有数字都是唯一的,并且每个数字都在范围0到n-1之内。在范围0到n-1的n个数字中有且只有一个数字不在该数组中,请找出这个数字。

    思路 

      由于数组是单调递增排序的,因此我们可以尝试二分查找算法来进行优化。假设我们某一步抵达数组中的第i个数字。如果我们很幸运,该数字的值刚好也是i,那么我们就找到了一个数字和其下标相等。

    当数字的值和下标不相等的时候,假设数字的值为m。先考虑m大于i的情形,即数字的值大于它的下标。由于数组中的所有数字都唯一并且单调递增,那么对于任意大于0的k,位于下标i+k的数字的值大于或等于m+k。另外,因为m>i,所以m+k>i+k。因此,位于下标i+k的数字的值一定大于它的下标。这意味着如果第i个数字的值大于i,那么它的右边的数字都大于对应的下标,我们都可以忽略。下一轮查找只需要从它左边的数字中查找即可。

      数字的值m小于它的下标i的情形和上面类似。它左边的所有数字的值都小于对应的下标,也可以忽略。

    也可以这样想:

      先获取数组的中间数,若中间数的值和下标相等,则找到一个满足条件的数;若中间数的值大于它的下标,则根据数组递增的特性可知:中间元素至少比他的下标大1,而且中间数以后的元素的值至少每个比前面大1,同时它们的下标每次也是增加1,所以右边的这些元素的值都大于它们的下标(至少大1),因此需要继续在左边寻找。同理,若中间数的值小于它的下标,则中间数左边的那些元素的值也都小于它们的下标,因此需要继续在右边寻找。

    #include <iostream>
    #include <vector>
    using namespace std;
    
    class Solution
    {
        public:
            int get_num_same_as_index(const vector<int> &v);
    };
    int Solution::get_num_same_as_index(const vector<int> &v)
    {
        if(v.empty()||v.size()<0)
            return -1;
        int left=0;
        int right=v.size()-1;
        while(left<=right)
        {
            int mid=(left+right)>>1;
            if(v[mid]==mid)
                return mid;
            if(v[mid]>mid)
                right=mid-1;
            else
                left=mid+1;
        }
    }
    int main()
    {
        vector<int> v{-3,-1,1,3,5};
        Solution s;
        cout<<s.get_num_same_as_index(v)<<endl;
        return 0;
    }
  • 相关阅读:
    Python的logging模块
    Python中的json模块
    Python的re模块
    NoSQL简介
    单例设计模式
    基于配置文件的方式配置AOP
    重用切点表达式
    切面优先级
    返回通知、异常通知、环绕通知
    后置通知
  • 原文地址:https://www.cnblogs.com/tianzeng/p/10262126.html
Copyright © 2011-2022 走看看