二分查找的原理与实现
- 对于已经基于排序规则排好序的容器
- 每次查找都能使范围缩小
自写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;