当前位置:   article > 正文

二分查找模板_check(mid)

check(mid)

二分查找

(一)整数二分

二分的本质不是单调性,没有单调性也可以用二分
二分的本质是边界,在区间上定义某种性质,使得整个区间一分为二,一半区间满足这种性质,另一半区间不满足这种性质,那么二分可以寻找到性质的边界
在这里插入图片描述
既可寻找绿颜色边界,也可寻找红颜色边界
红色为不满足性质的区间,绿色为满足性质的区间

一.找满足性质的边界值(即绿色区间边界值)

1.找中间值mid=(l+r)>>1;
2.check(mid)满足性质时为true
3.如果check(mid)=true
说明在右边区间(绿色)找到了mid,应该到[l.mid]区间找答案
如果check(mid)=false
说明在左边区间(红色)找到了mid,应该到[mid+1,r]区间找答案

while(l<r){
    int mid=(l+r)>>1;
    if(check(mid)==true)r=mid
    else  l=mid+1;
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

二.找不满足性质的边界值(即红色区间边界值)

1.找中间值mid=(l+r+1)>>1;
当l=r-1时, 如果check为true l=mid mid=l+r>>1=l 会发生死循环
2.check(mid)不满足性质时为true
3.如果check(mid)=true
说明在左边区间(红色)找到了mid,应该到[mid,r]区间找答案
如果check(mid)=false
说明在右边区间(绿色)找到了mid,应该到[l,mid-1]区间找答案

while(l<r){
    int mid=(l+r+1)>>1;
    if(check(mid)l=mid;
    else r=mid-1;

}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

归结上面的两种二分方法

步骤为:

先写一个check函数
判定在check的情况下(true和false的情况下),如何更新区间。
在check(m)==true的分支下是:
根据是l-mid还是r=mid判断到底是红色方法还是绿色方法
l=mid的情况, mid+1,中间点的更新方式是m=(l+r+1)/2
r=mid的情况,mid不用+1,中间点的更新方式是m=(l+r)/2
这种方法保证了:

  1. 最后的l==r
  2. 搜索到达的答案是闭区间的,即a[l]是满足check()条件的。

二分问题一定有解,通过二分判断出的性质导出原问题可能会无解。

例:check为>=t的l,r
返回第一个位置

int search(int t,int l,int r){
    while(l<r){
        int mid=(l+r)>>1;
        if(num[mid]>=t)r=mid;
        else l=mid+1;
    }
    printf("%d %d\n",l,r);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

结果为:
在这里插入图片描述

例:check为<=t的l,r
返回最后一个位置

int searchr(int t,int l,int r){
    while(l<r){
        int mid=(l+r+1)>>1;
        if(num[mid]<=t)l=mid;
        else r=mid-1;
    }
    
    printf("%d %d\n",l,r);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

在这里插入图片描述

最终返回结果l=r

例题 数的范围

在这里插入图片描述

#include <iostream>
using namespace std;
const int N=1e6;
int num[N];
int searchl(int t,int l,int r){
    while(l<r){
        int mid=(l+r)>>1;
        if(num[mid]>=t)r=mid;
        else l=mid+1;
    }
    if(num[l]!=t)return -1;
    else return l;
}
int searchr(int t,int l,int r){
    while(l<r){
        int mid=(l+r+1)>>1;
        if(num[mid]<=t)l=mid;
        else r=mid-1;
    }
    if(num[l]!=t)return -1;
    else return l;
}
int main(){
    int n,q;
    scanf("%d%d",&n,&q);
    
    for(int i=0;i<n;i++){
       scanf("%d",&num[i]);
    }
    
    for(int i=0;i<q;i++){
        int t;
        scanf("%d",&t);
        int l=searchl(t,0,n-1);
        int r=searchr(t,0,n-1);
        if(l==-1||r==-1)printf("-1 -1\n");
        else printf("%d %d\n",l,r);
    }
    return 0;
    
    
    
    
    
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45

(二) 浮点数二分

相对于整数二分没有边界问题,很好写

法一:用精度进行限制

while(r-l<1e-6){
    int mid=(l+r)>>1;
    if(check(mid))r=mid;
    else l=mid;
}
  • 1
  • 2
  • 3
  • 4
  • 5

法二:直接迭代100次

for(int i=0;i<100;i++){
    int mid=(l+r)>>1;
    if(check(mid))r=mid;
    else l=mid;
}
  • 1
  • 2
  • 3
  • 4
  • 5

例题 求数的三次方根

注意范围取值
当x^3=0.001 x=0.1
不能取[-x,x]作为区间,取max(1,x)
在这里插入图片描述

#include <iostream>
#include <math.h>
using namespace std;
double search(double x,double l,double r){
    
    while(r-l>=1e-7){
        double mid=(l+r)/2.0;
        if(mid*mid*mid>=x)r=mid;
        else l=mid;
    }
    return l;
}
int main(){
    double x;
    scanf("%lf",&x);
    double l=min(-1.0,x);
    double r=max(1.0,x);
    double ans=search(x,l,r);
    printf("%.6lf",ans);
    
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/小蓝xlanll/article/detail/386061
推荐阅读
相关标签
  

闽ICP备14008679号