二分查找写法总结

来源:互联网 发布:照片模板软件 编辑:程序博客网 时间:2024/06/06 05:57

二分查找,是通过二分区间不断的缩小查找范围,已达到快速查找的目的。在ACM中,二分查找经常穿插在各类题型中,用途很广泛,是必须要掌握的一个算法。

二分查找的对象

一般二分查找都是对有单调性的序列操作,序列的数据类型包括整型和浮点型

  • 整型

    1. 常用写法分类
      • 取整方式:向下取整、向上取整
      • 区间开闭:闭区间、左闭右开区间、左开右闭区间、开区间
      • 问题类型:
        1. 对于不下降序列a,求最小的i,使得a[i] = key,即查找序列中等于key值的第一个位置,等同于c++里的lower_bound;
        2. 对于不下降序列a,求最大的i,使得a[i] = key,即查找序列中等于key值的最后一个位置,等同于c++里的upper_bound;
        3. 对于不下降序列a,求最小的i,使得a[i] > key,即最小化最大值;
        4. 对于不下降序列a,求最大的i,使得a[i] < key,即最大化最小值;
        5. 对于不下降序列a,求最小的i,使得a[i] >= key;
        6. 对于不下降序列a,求最大的i,使得a[i] <= key;
        7. 对于不上升序列a,求最小的i,使得a[i] = key
        8. 对于不上升序列a,求最大的i,使得a[i] = key
        9. 对于不上升序列a,求最小的i,使得a[i] > key
        10. 对于不上升序列a,求最大的i,使得a[i] < key
        11. 对于不上升序列a,求最小的i,使得a[i] >= key;
        12. 对于不上升序列a,求最大的i,使得a[i] <= key;

    一般会先用sort排序预处理序列,默认升序,因此这里只列举前种写法。

  • 对于不下降序列a,求最小的i,使得a[i] = key,即查找序列中等于key值的第一个位置,等同于c++里的lower_bound

//找第一个等于key的位置int Binary_search1(int a[],int n,int key){    int mid,l=0,r=n-1;//区间[0,n-1]    while(l<r)    {        mid=l+((r-l)>>1);//向下取整        if(a[mid]<key) l=mid+1;        else r=mid;    }    if(a[r]==key) return r;//在区间右端点终止    return -1;//不存在}
  • 对于不下降序列a,求最大的i,使得a[i] = key,即查找序列中等于key值的最后一个位置,等同于c++里的upper_bound
//找最后一个等于key的位置int Binary_search2(int a[],int n,int key){    int mid,l=0,r=n-1;//区间[0,n-1]    while(l<r)    {        mid=l+((r+1-l)>>1);//向上取整        if(a[mid]<=key) l=mid;        else r=mid-1;    }    if(a[l]==key) return l;//在区间左端点终止    return -1;//不存在}
  • 对于不下降序列a,求最小的i,使得a[i] > key,即最小化最大值
//找第一个大于key的位置int Binary_search3(int a[],int n,int key){    int mid,l=0,r=n-1;//区间[0,n-1]    while(l<r)    {        mid=l+((r-l)>>1);//向下取整        if(a[mid]<=key) l=mid+1;        else r=mid;    }    if(a[r]>key) return r;//在区间右端点终止    return -1;//不存在}
  • 对于不下降序列a,求最大的i,使得a[i] < key,即最大化最小值
//找最后一个小于key的位置int Binary_search4(int a[],int n,int key){    int mid,l=0,r=n-1;//区间[0,n-1]    while(l<r)    {        mid=l+((r+1-l)>>1);//向上取整        if(a[mid]<key) l=mid;        else r=mid-1;    }    if(a[l]<key) return l;//在区间左端点终止    return -1;//不存在}
  • 对于不下降序列a,求最小的i,使得a[i] >= key
//找第一个大于或等于key的位置int Binary_search5(int a[],int n,int key){    int mid,l=0,r=n-1;//区间[0,n-1]    while(l<r)    {        mid=l+((r-l)>>1);//向下取整        if(a[mid]<key) l=mid+1;        else r=mid;    }    if(a[r]>=key) return r;//在区间右端点终止    return -1;//不存在}
  • 对于不下降序列a,求最大的i,使得a[i] <= key
//找最后一个小于等于key的位置int Binary_search6(int a[],int n,int key){    int mid,l=0,r=n-1;//区间[0,n-1]    while(l<r)    {        mid=l+((r+1-l)>>1);//向上取整        if(a[mid]<=key) l=mid;        else r=mid-1;    }    if(a[l]<=key) return l;//在区间左端点终止    return -1;//不存在}
  • 更保险的写法,不用管最后是取左值还是右值,推荐使用!
    例如:对于不下降序列a,求最小的i,使得a[i] >= key,即查找序列中大于或等于key值的第一个位置,等同于c++里的lower_bound
//找第一个大于或等于key的位置int Binary_search7(int a[],int n,int key){    int mid,l=0,r=n-1,ans=-1;//区间[0,n-1]    while(l<=r)    {        mid=l+((r-l)>>1);//向下取整        if(a[mid]<key) l=mid+1;        else        {            ans=mid;//不断向下更新            r=mid-1;        }    }    return ans;}
  • 对于不下降序列a,求最大的i,使得a[i] <= key
//找最后一个小于或等于key的位置int Binary_search8(int a[],int n,int key){    int mid,l=0,r=n-1,ans=-1;//区间[0,n-1]    while(l<=r)    {        mid=l+((r-l)>>1);//向下取整        if(a[mid]<=key)        {            ans=mid;//不断向上更新            l=mid+1;        }        else r=mid-1;    }    return ans;}
  • 浮点型
    一般用于使结果达到某一精度而不断地进行二分,也有两种写法,一种是指定循环次数作为终止条件,1次循环可以把区间的范围缩小一半,100次的循环则可以达到 21001030的精度范围,这样做应该是没有问题的,而且不会陷入无限循环;另一种是指定精度为eps作为终止条件,但是这样做需要注意,如果eps取得太小了,就有可能因为浮点小数精度的原因导致陷入死循环。

  • 指定精度

#define eps 1e-8double Binary_search(double l, double r){    double mid;    while (r-l>=eps)    {        mid=(l+r)/2.0;        if(judge(mid)) r=mid;        else l=mid;  }  return mid;}
  • 指定循环次数
double Binary_search(double l, double r){    double mid;    for(int i=0; i<100; i++)    {        mid=(l+r)/2.0;        if(judge(mid)) r=mid;        else l=mid;    }    return mid;}

实际测试一下

上代码:

#include<iostream>#include<cstdio>using namespace std;//找第一个等于key的位置int Binary_search1(int a[],int n,int key){    int mid,l=0,r=n-1;//区间[0,n-1]    while(l<r)    {        mid=l+((r-l)>>1);//向下取整        if(a[mid]<key) l=mid+1;        else r=mid;    }    if(a[r]==key) return r;//在区间右端点终止    return -1;//不存在}//找最后一个等于key的位置int Binary_search2(int a[],int n,int key){    int mid,l=0,r=n-1;//区间[0,n-1]    while(l<r)    {        mid=l+((r+1-l)>>1);//向上取整        if(a[mid]<=key) l=mid;        else r=mid-1;    }    if(a[l]==key) return l;//在区间左端点终止    return -1;//不存在}//找第一个大于key的位置int Binary_search3(int a[],int n,int key){    int mid,l=0,r=n-1;//区间[0,n-1]    while(l<r)    {        mid=l+((r-l)>>1);//向下取整        if(a[mid]<=key) l=mid+1;        else r=mid;    }    if(a[r]>key) return r;//在区间右端点终止    return -1;//不存在}//找最后一个小于key的位置int Binary_search4(int a[],int n,int key){    int mid,l=0,r=n-1;//区间[0,n-1]    while(l<r)    {        mid=l+((r+1-l)>>1);//向上取整        if(a[mid]<key) l=mid;        else r=mid-1;    }    if(a[l]<key) return l;//在区间左端点终止    return -1;//不存在}//找第一个大于或等于key的位置int Binary_search5(int a[],int n,int key){    int mid,l=0,r=n-1;//区间[0,n-1]    while(l<r)    {        mid=l+((r-l)>>1);//向下取整        if(a[mid]<key) l=mid+1;        else r=mid;    }    if(a[r]>=key) return r;//在区间右端点终止    return -1;//不存在}//找最后一个小于等于key的位置int Binary_search6(int a[],int n,int key){    int mid,l=0,r=n-1;//区间[0,n-1]    while(l<r)    {        mid=l+((r+1-l)>>1);//向上取整        if(a[mid]<=key) l=mid;        else r=mid-1;    }    if(a[l]<=key) return l;//在区间左端点终止    return -1;//不存在}//找第一个大于或等于key的位置int Binary_search7(int a[],int n,int key){    int mid,l=0,r=n-1,ans=-1;//区间[0,n-1]    while(l<=r)    {        mid=l+((r-l)>>1);//向下取整        if(a[mid]<key) l=mid+1;        else        {            ans=mid;//不断向下更新            r=mid-1;        }    }    return ans;}//找最后一个小于或等于key的位置int Binary_search8(int a[],int n,int key){    int mid,l=0,r=n-1,ans=-1;//区间[0,n-1]    while(l<=r)    {        mid=l+((r-l)>>1);//向下取整        if(a[mid]<=key)        {            ans=mid;//不断向上更新            l=mid+1;        }        else r=mid-1;    }    return ans;}int main(){    int a[]={1, 1, 2, 2, 4, 4, 8, 8, 10, 10};    int n=10;    int key=8;    cout<<"下标:";    for(int i=0; i<n; i++)        cout<<" "<<i;    cout<<endl<<"序列:";    for(int i=0; i<n; i++)        cout<<" "<<a[i];    cout<<endl;    cout<<"第一个等于"<<key<<"的位置:"<<Binary_search1(a,n,key)<<endl;    cout<<"最后一个等于"<<key<<"的位置:"<<Binary_search2(a,n,key)<<endl;    cout<<"第一个大于"<<key<<"的位置:"<<Binary_search3(a,n,key)<<endl;    cout<<"最后一个小于"<<key<<"的位置:"<<Binary_search4(a,n,key)<<endl;    cout<<"第一个大于或等于"<<key<<"的位置:"<<Binary_search5(a,n,key)<<endl;    cout<<"最后一个小于或等于"<<key<<"的位置:"<<Binary_search6(a,n,key)<<endl;    cout<<"第一个大于或等于"<<key<<"的位置:"<<Binary_search7(a,n,key)<<endl;    cout<<"最后一个小于或等于"<<key<<"的位置:"<<Binary_search8(a,n,key)<<endl;    return 0;}

测试结果:
测试结果

总结一下

二分有几个需要注意的地方:
1. 数列中相同值的处理;
2. 循环不变式的终止条件;
3. 边界条件的判定。

写法很多,建议只记一种,用的时候灵活变通就好。

原创粉丝点击