二分查找刷题总结

跑深度学习的时候也没事干 刷刷题
二分学了好多次,每次都是学了忘忘了又学,发个博客当笔记了
查找某个具体数的模板,这肯定是学二分时的第一个模板,没什么好说的

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

但是问题复杂时,这套方法就不灵了,每次看懂了,过段时间又忘了,所以总结一下
二分搜索解题两大原则1.每次都要缩减搜索区域2.每次缩减不能排除潜在答案
三大模板
1.找一个准确值
循环条件:l<=r
缩减搜索空间:l = mid+1,r = mid-1
2.找一个模糊值(比4大的数)
循环条件:l<r
缩减搜索空间:l=mid,r=mid-1或者l=mid+1,r=mid
3.万用形
循环条件:l<r-1
缩减搜索空间:l=mid,r=mid

leecode278.第一个错误的版本
实际上就是找出第一个出错的版本,出错版本后的版本一定出错

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

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

思路:因为每次都要缩减搜索空间,所以当isBadVersion返回true时,他的右边一定都为出错版本,左边还有可能有出错版本,他自己也可能是第一个出错的版本,所以r=mid,不可以排除潜在答案。而当isBadVersion返回false时,这个版本一定为正确版本,此时可以排除这个答案所以,l=mid+1

leecode410.分割数组最大值
给一个非负整数数组和一个整数m,将数组分成连续的m个非空连续数组,使得这n个连续数组的和的最大值最小。输出这个最小的最大和。

class Solution {
public:
    bool split(vector<int>& nums,int sum,int k){
        int temp_sum=0;
        int count = 1;
        for(int i=0;i<nums.size();i++){
            if(temp_sum + nums[i]>sum){
                count++;
                temp_sum =nums[i];
            }else{
                temp_sum +=nums[i];
            }
        }
        return count<=k;
    }
    int splitArray(vector<int>& nums, int k) {
        int sum=0;
        int maxx=0;
        for(int i=0;i<nums.size();i++){
            sum+=nums[i];
            maxx = max(maxx,nums[i]);
        }
        int l=maxx;
        int r=sum;
        while(l<r){
            int mid = (l+r)>>1;
            if(split(nums,mid,k)){
                r = mid;
            }else{
                l =mid+1;
            }
        }
        return l;
    }
};

这道题转换思路,实际上是对要求的最大和进行二分。split返回true时,分出的数组小于或等于要求的m,此时不可以排除潜在答案,所以r=mid。split返回false时,分出的数组大于要求的m,证明我们的最大和过小,并且此时不可能为最后的解,l=mid+1.
leecode852.山脉数组的峰顶索引

class Solution {
public:
    int peak(vector<int>& arr,int n){
        if(n==0){
            return 2;
        }
        if(n==arr.size()-1){
            return -2;
        }
        if(arr[n-1]<arr[n] && arr[n]<arr[n+1]){
            return 2;
        }
        if(arr[n-1]>arr[n] && arr[n]>arr[n+1]){
            return -2;
        }
        if(arr[n-1]<arr[n] && arr[n]>arr[n+1]){
            return 1;
        }
        return 0;
    }
    int peakIndexInMountainArray(vector<int>& arr) {
        int l=0;
        int r=arr.size()-1;
        int mid;
        while(l<=r){
            mid=l+(r-l)/2;
            if(peak(arr,mid)==1){
                return mid;
            }
            if(peak(arr,mid)==2){
                l=mid+1;
            }
            if(peak(arr,mid)==-2){
                r=mid-1;
            }
        }
        return mid;
    }
};

这题还挺简单的,是精确搜索,返回1代表找到了,返回2代表要往右寻找,返回-2代表要往左寻找,但是我写麻烦了,简便写法如下

class Solution {
public:
    int peakIndexInMountainArray(vector<int>& arr) {
        int l=1;
        int r=arr.size()-2;
        int mid=0;
        while(l<r){
            mid=(l+r)/2;
            if(arr[mid]<arr[mid+1]){
                l=mid+1;
            }else{
                r=mid;
            }
        }
        return l;
    }
};

可视为模糊查找,查找第一个arr[mid]>arr[mid+1]的数,而n == 0 || n == arr.size()-1的情况直接被排除了,不可能为峰顶元素。

leecode1292.元素和小于等于阈值的正方形的最大边长
二分答案结合二维前缀和

class Solution {
public:

    int maxSideLength(vector<vector<int>>& mat, int threshold) {
        int m = mat.size();
        int n = mat[0].size();
        vector<vector<int>> summ(m + 1, vector<int>(n + 1));
        for(int i=1;i<=m;i++){
            for(int j=1;j<=n;j++){
                summ[i][j]=summ[i-1][j]+summ[i][j-1]-summ[i-1][j-1]+mat[i-1][j-1];
                // cout<<summ[i][j]<<endl;
            }
        }
        // return maxMat(mat,threshold,2);
        int l = 0;
        int r = min(mat.size(),mat[0].size());
        int mid;
        int sum;
        while(l<r){
            mid = l+(r-l+1)/2;
            int flag=0;
            for(int i=0;i+mid<=mat.size();i++){
                for(int j=0;j+mid<=mat[0].size();j++){
                    sum = summ[i+mid][j+mid]-summ[i][j+mid]-summ[i+mid][j]+summ[i][j];
                    if(sum<=threshold){
                        flag=1;
                    }
                }
            }
            sum=0;
            if(flag){
                l=mid;
            }else{
                r=mid-1;
            }
        }
        return l;
    }
};

文章借鉴https://www.bilibili.com/video/BV1Ng4y1q7E3/?spm_id_from=333.337.search-card.all.click&vd_source=7ff09f6ed463c54f8768c5148846a8e5

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值