二分查找基础

二分查找的原理与实现

  • 对于已经基于排序规则排好序的容器
  • 每次查找都能使范围缩小

自写binary_search

  • 在一个大小为size的数组里,寻找元素p,找到返回元素下标;否则返回-1;
int mybinarysearch(int a[],int size,int p){
    int L=0;//查找区间的左端点
    int R=size-1;//查找区间的右端点
    while(L<R){
        int mid=(R+L)/2;//取正中元素的下标
        if(p==a[mid]){
            return mid;
        }else if(p>a[mid]){
            L=mid+1;//设置新的查找区间的左端点
        }else{
            R=mid-1;//设置新的查找区间的右端点
        }
    }
    return -1;
}

自写LowerBound

  • 在一个从小到大排序的大小为size的数组里,寻找比p小的最大元素的下标;
int mylowerbound(int a[],int size,int p){
    int L=0;//查找区间的左端点
    int R=size-1;//查找区间的右端点
    int result=-1;//目前为止的最优解(最大的下标
    while(L<R){
        int mid=(R+L)/2;
        if(a[mid]>=p){
            R=mid-1;//那目标元素肯定在左半边,因此要把右端点往左
        }else{
            result=mid;//说明找到一个答案,为目前下标最大的最优解
            L=mid+1;//再往下标更大的地方找
        }
    }
    return result;
}

查找中间区间小技巧

int mid=(L+R)/2;
int mid=L+(R-L)/2;
//为了防止L+R过大溢出,可以养成习惯写第二个

例题——二分法求方程的根

  • 求下面方程的一根:f(x)=x3-5x2+10x-80=0

    若求出的根是a,则要求|f(a)|<=10^-6

    即非精确根

  • 解法:求导可知单调递增,而且f(0)<0和f(100)>0

    因此可以使用二分法找到根;

二分法求方程根的方程特点

  • 方程在二分区间是单调区间
  • 区间端点相乘<0
#include<iostream>
#include<cmath>
#include<cstdlib>
using namespace std;
double EPS = 1e-6;
double f(double x){return x*x*x-5*x*x+10*x-80;}
int main(){
    double root,x1=0,x2=100,y;
    root=x1+(x2-x1)/2;
    int triedTimes=1;//记录尝试的次数,对求根不是必须的
    y=f(root);
    while(fabs(y)>EPS){
        if(y>0) x2=root;
        else    x1=root;
        root=x1+(x2-x1)/2;
        y=f(root);
        triedTimes++;
    }
    cout<<root<<endl;
    cout<<triedTimes<<endl;
}

例题——找一对数

  • 描述:输入n个数(n<=100,000)个整数,找出其中的两个数,它们之和等于整数m(假定肯定有解)。题中所有整数都能用int表示
/*方法一:循环固定一个数n,二分查找goal-n;*/
#include<iostream>
#include<algorithm>
using namespace std;
int mybinarysearch(int a[],int size,int p){
    int L=0;//查找区间的左端点
    int R=size-1;//查找区间的右端点
    while(L<R){
        int mid=(R+L)/2;//取正中元素的下标
        if(p==a[mid]){
            return mid;
        }else if(p>a[mid]){
            L=mid+1;//设置新的查找区间的左端点
        }else{
            R=mid-1;//设置新的查找区间的右端点
        }
    }
    return -1;
}
int main(){
    int num[100000],n,i=0,goal;
    cin>>goal;
    while(cin>>n){
        num[i]=n;
        i++;
    }
    sort(num,num+i);
    for(int j=0;j<i;j++){
        int pos=mybinarysearch(num,i,goal-num[j]);
        if(pos!=-1){
            cout<<num[j]<<" "<<num[pos]<<endl;
            break;
        }
    }
}
/*方法二:数组排序后,查找是一个left和一个right,看a[left]+a[right]
如果大于m,就让right-1;如果小于m,就让left+1,直到a[left]+a[right]=goalnum*/

例题——农夫和奶牛

  • 题述:农夫John建造了一座很长的畜栏,它包括N(2<=N<=100,000)个隔间,这些小隔间的位置为X0,……,Xn(0<=xi<=1,000,000,000,均为整数,各不相同)

    John的C(2<=C<=N)头牛每头分到一个隔间。牛都希望互相离得远点省的互相打扰。

    • 怎样才能使任意两头牛之间的最小距离尽可能的大,这个最大的最小距离是多少?
/*二分思路:排序隔间坐标后
  在[L,R]内用二分法尝试“最大最近距离”D=L+(R-L)/2
  L,R的初始值就是[1,1000000000/c]
  若D可行,记录下当下的最优解D,然后继续[L,R] L=D+1
  若D不可行,说明这个D太大了,R=D-1*/
  //可不可行的判断是O(N)
  //时间复杂度是:log(1,000,000,000/c)*N;

二分查找的题目中,往往会表明数据规模

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Caaaaaan

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值