一、整数二分
主要思想:根据题目要求写出一个check函数,当中间位置的值满足条件时,将子序列其中一个端点变为中间位置,若不满足条件则改变另一个端点,每次都使要检查的序列长度变为原来的一半,且答案仍然包含在子序列中。多次递归后,子序列长度为1,即要查找的值。
例题:给出一个从小到大排序好的长为n的序列,查找m个值x,输出第一次出现x的位置以及最后一次出现x的位置,如果x不存在,则输出"-1 -1"。
int main(){
scanf("%d %d",&n,&m);
for(int i=0;i<n;i++) scanf("%d",&a[i]);
while(m--){
scanf("%d",&x);
int l=0,r=n-1;
while(l<r){ //长度不为1,继续二分
int mid=(l+r)/2; //【1】
if(a[mid]>=x) r=mid; //【2】
else l=mid+1;
}
if(x!=a[l]) cout<<"-1 -1"<<endl; //x不存在的情况
else{
cout<<l<<" "; //输出放到else里确保x存在才输出
int l=0,r=n-1;
while(l<r){
int mid=(l+r+1)/2;
if(a[mid]<=x) l=mid;
else r=mid-1;
}
cout<<l<<endl;
}
}
return 0;
}
【1】:当r=mid时,mid为(l+r)/2,当l=mid时,mid为(l+r+1)/2。因为除法总是向下取整,l=r-1时mid=l,使得后面令l=mid没有改变l,陷入死循环,加1后确保向上取整。
【2】:每次输出的都是l,题目又要求先输出第一次出现的值,故第一次二分法的check函数为a[mid]>=x,从右边逼近。
二、浮点数二分
主要思想:与整数二分类似,浮点数二分的递归判定条件不再是l<r,而是r与l的差大于一个很小的浮点数,且浮点数除法不需要考虑向下取整的情况,没有边界问题。
例题:求一个数的平方根。
int main(){
double n;
cin>>n; //输入输出浮点数时用cin和cout更方便
double l=0,r=n;
while(r-l>1e-4){ //一般比题目要求精度值多2
double mid=(r+l)/2;
if(mid*mid<=n) l=mid; //没有边界问题
else r=mid;
}
cout<<l<<endl;
return 0;
}