17道题带你理解二分查找

Posted 被折叠的小饼干

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了17道题带你理解二分查找相关的知识,希望对你有一定的参考价值。

不太会的可以先看看二分查找的模板
点这里

最需要注意的是二分查找的分界线并不止局限于大于等于小于,而是对某种性质的一种分界

1.Leetcode704. 二分查找

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

2.Leetcode69. Sqrt(x)

class Solution 
public:
    int mySqrt(int x) 
       long long l=0,r=x;
       while(r>l)
           long long mid=(l+r+1)/2;
           if(mid<=x/mid)
               l=mid;
           else
               r=mid-1;
           
         
       return l;
    
;

3.Leetcode374. 猜数字大小

class Solution 
public:
    int guessNumber(int n) 
        long long  l=1,r=n;
        while(l<r)
            long long  mid=(l+r)/2;
            if(guess(mid)==0||guess(mid)==-1)
                r=mid;
            else 
                l=mid+1;
            
        
        return r;

    
;

4.Leetcode153. 寻找旋转排序数组中的最小值


class Solution 
public:
    int findMin(vector<int>& nums) 
       int len=nums.size();
       int l=0,r=len-1;
       int high=nums[len-1];
       while(l<r)
           int mid=(l+r)/2;
           if(nums[mid]<=high)
               r=mid;
           else
               l=mid+1;
           
       
       return nums[l];
    
;

5.Leetcode33. 搜索旋转排序数组


这道题很特别,相较于Leetcode153.寻找旋转排序数组中的最小值
这里找的是目标值target,而不是nums[0],但是target对于153中nums[0]划分的界限(利用最后一个数x作为分界点,小于等于x在nums[0]右边,大于x在nums[0]左边)来说,在这里难以找到循环数组中target是什么性质的分界线,还是需要借助nums[0]和target的位置关系,直接分情况讨论

class Solution 
public:
    int search(vector<int>& nums, int target) 
        int len=nums.size(),ans;
        int st=find1(nums,0,len-1,nums[len-1]);
        printf("st=%d\\n",st);
        if(nums[len-1]>=target)
            ans=find2(nums,st,len-1,target);
        else
            ans=find2(nums,0,st-1,target);
         

        if(nums[ans]!=target)return -1;
        return ans;
        
    
    //查找nums[0]的位置,nums[0]的左边都>nums[len-1],右边都小于等于nums[len-1]
    int  find1(vector<int>&nums,int l,int r,int divide)
        while(l<r)
            int mid=(l+r)/2;
            if(nums[mid]<=divide)
                r=mid;
            else
                l=mid+1;
            
        
        return l;

    
    //查找target位置,此时查找区间一定是递增的
    int find2(vector<int>&nums,int l,int r,int divide)
        while(l<r)
            int mid=(l+r)/2;
            if(nums[mid]>=divide)
                r=mid;
            else
                l=mid+1;
            
        
        return l;

    
;

6.Leetcode278. 第一个错误的版本

// The API isBadVersion is defined for you.
// bool isBadVersion(int version);

class Solution 
public:
    int firstBadVersion(int n) 
        long long l=1,r=n;
        while(l<r)
            long long mid=(l+r)/2;
            if(isBadVersion(mid))
                r=mid;
            else
                l=mid+1;
            
        
        return l;
    
;

7.Leetcode162. 寻找峰值


参考Leetcode作者:guanpengchn
首先要注意题目条件,在题目描述中出现了 nums[-1] = nums[n] = -∞,这就代表着只要数组中存在一个元素比相邻元素大,那么沿着它一定可以找到一个峰值
往递增的方向上,二分,一定能找到波峰,往递减的方向只是可能找到,也许没有。
根据上述结论,我们就可以使用二分查找找到峰值
查找时,左指针 l,右指针 r,以其保持左右顺序为循环条件
根据左右指针计算中间位置 m,并比较 m 与 m+1 的值,如果 m 较大,则左侧存在峰值,r = m,如果 m + 1 较大,则右侧存在峰值,l = m + 1

时间复杂度O(logN)

class Solution 
public:
    int findPeakElement(vector<int>& nums) 
        int len=nums.size();
        int l=0,r=len-1;
        int ans;
        while(l<r)
            int mid=(l+r)/2;
            if(nums[mid]>nums[mid+1])
                r=mid;
            else
                l=mid+1;
            
        
        return l;
    
;

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

class Solution 
public:
    vector<int> searchRange(vector<int>& nums, int target) 
         vector<int>ans;
         if(nums.size()==0)
             ans.push_back(-1);
             ans.push_back(-1);
             return ans;
         
         int l=findl(nums,target);
         int r=findr(nums,target);
         if(l==-1||r==-1)
            ans.push_back(-1);
            ans.push_back(-1);
         else
             ans.push_back(l);
             ans.push_back(r);
         
         return ans;

    
    int findl(vector<int>&nums,int target)
        int l=0;int r=nums.size()-1;
        while(l<r)
            int mid=(l+r)/2;
            if(nums[mid]>=target)
                r=mid;
            else
                l=mid+1;
            
        
        if(nums[l]==target)return l;
        return -1;
    
    int findr(vector<int>&nums,int target)
        int l=0;int r=nums.size()-1;
        while(l<r)
            int mid=(l+r+1)/2;
            if(nums[mid]<=target)
                l=mid;
            else
                r=mid-1;
            
        
        if(nums[l]==target)return l;
        return -1;
    
;

9.Leetcode658. 找到 K 个最接近的元素


法一:直观地,我们可以将数组中的元素按照与目标 x 的差的绝对值排序,排好序后前 k 个元素就是我们需要的答案。
法二:
①如果target<nums[0],返回前k个数
②如果target>nums[len-1],返回后k个数
③利用二分找到最接近的数,然后不断比较–left,–right与target的绝对值,直到找够k个最小的

class Solution 
public:
    const int N=1e4+10;
    vector<int>ans;
    vector<int> findClosestElements(vector<int>& arr, int k, int x) 
        int len=arr.size();
        if(x<=arr[0])
            int i=0;
            while(k--)
              ans.push_back(arr[i++]);
            
        else if(x>=arr[len-1])
            int i=len-1;
            while(k--)
              ans.push_back(arr[i--]);
            
        else
            int index=find(arr,x);
            //找到最接近的值的下标
            if(index-1>=0&&abs(arr[index-1]-x)<=abs(arr[index]-x))index--;//左优先,所以这里是小于等于
            if(index+1<len&&abs(arr[index+1]-x)<abs(arr[index]-x))index++;
           
            ans.push_back(arr[index]);
            int l=index-1,r=index+1;
            while(--k&&l>=0&&r<len)
                if(abs(arr[l]-x)<=abs(arr[r]-x))
                    ans.push_back(arr[l--]);
                else
                     ans.push_back(arr[r++]);
                
            
            while(k--)
                if(l>=0)ans.push_back(arr[l--]);
                if(r<len)ans.push_back(arr[r++]);
            
            

        
        sort(ans.begin(),ans.end());
        return ans;
        

    
    int find(vector<int>&arr,int x)
          int l=0,r=arr.size()-1;
          while(l<r)
              int mid=(l+r)/2;
              if(arr[mid]>=x)
                  r=mid;
              else
                  l=mid+1;
              
          
          return l;
    
;

10.Leetcode50. Pow(x, n)


这道用快速幂好一些

class Solution 
public:
    const double INF=0x3f3f3f3f;
    double myPow(double x, long n) 
       bool flag=false;
       if(n<0)flag=true;
       if(flag)n=-n;
       double res=qmi(x,n);
       if(flag)return 1/res;
       return res;
    
    double qmi(double m,long k)
       double res=1,t=m;
       while(k)
           if(k&1)res=res*(double)t;
           t=t*t;
           k=k>>1;
       
       return res;
    
;

11.Leetcode367. 有效的完全平方数

class Solution 
public:
    bool isPerfectSquare(int num) 
        long long l=1,r=num;
        while(l<r)
            long long mid=(l+r)/2;
            if(mid>=num/mid)r=mid;
            else l=mid+1;
        
        if(l*l==num)return true;
        return false;

    
;

12.Leetcode744. 寻找比目标字母大的最小字母

class Solution 
public:
    char nextGreatestLetter(vector<char>& letters, char target) 
        int len=letters.size();
        以上是关于17道题带你理解二分查找的主要内容,如果未能解决你的问题,请参考以下文章

十道题带你手撕二叉树

两道面试题带你解析 Java 类加载机制

10分钟带你理解算法中二分查找

5分钟带你领略:写一个二分查找为什么让面试者挂的这么惨?

两道面试题带你解析 Java 类加载机制

46道面试题带你了解高级Java面试,在线面试指南