怎么说呢,二分我之前就是太害怕它了,开始学的时候在知乎上到二分有64种写法,一下被吓着了,才一直搁到现在才学。
主要思想大家都明白,就是代码实现起来有问题。
个人认为,二分最后可以归结为三种问题(假设序列不下降,参数均为区间[l,r]):
1. 是否存在x。
如果A[mid]==x 即为找到,返回mid位置即可;
如果A[mid]>x说明当前位置大了,可以确定x在[l,mid-1]区间内,因此 执行r = mid-1即可;
如果A[mid]<x说明当前位置小了,可以确定x在[mid+1,r]区间内,因此执行l = mid+1即可;
int binary_sreach(int A[],int l,int r,int x){
int mid;
while (l<=r) {
mid = (l+r)>>1;
if(A[mid]==x)
return mid;
else if(A[mid]>x)
r = mid-1;
else
l = mid+1;
}
return -1;
}
2. 找到第一个大于等于x的数。
1.这里的二分结束判断的条件是l<r,也就是当l==r时,我们就确定了唯一一个位置,如果这个位置是x的话,那么他就是
第一个大于等于x的值如果不是x的话,那么证明,序列中不存在x,而这时这个位置的意义是如果把x插入序列的话,
它应该在的位置。
2.如果A[mid]>=x说明第一个大于等于x的位置一定在mid处或mid的左侧,因此应该去左边区间[l,mid]寻找,即,r = mid;
3.如果A[mid]<x说明第一个大于等于x的一定在mid的右侧,因此应该去右边区间[mid+1,r]寻找,即,l = mid+1;
4.因为二分结束的条件一定是l==r 所以返回l或r都可以;
int lower_search(int A[],int l,int r,int x){
int mid;
while(l<r){
mid = (l+r)>>1;
if(A[mid]>=x)
r = mid;
else
l = mid+1;
}
return l;
}
3.找到第一个大于x的数。
1.与上边类似,判断条二分结束的条件为l<r;如果x不存在序列中的话,则和第二种情况一样,返回的是x应该在的位置;
2.如果A[mid]>x说明第一个大于x的位置一定在mid处或mid的左侧。因此应该去左边区间[l,mid]寻找,,即,r = mid;
3.如果A[mid]<=x说明第一个大于x的位置一定在mid的右侧。因此应该去左边区间[mid+1,r]寻找,即 l = mid;
4.上同,返回l或r都行。
int upper_sreach(int A[],int l,int r,int x){
int mid;
while(l<r){
mid = (l+r)>>1;
if(A[mid]>x)
r = mid;
else
l = mid+1;
}
return l;
}
注意代码2与代码3的区别,仅仅是判断条件不同,所以就可以存自己的二分模板了。
做题的时候经常遇见一些二分整个答案区间,直接找出符合条件的最小值或最大值。
题目举例:求sqrt(2)的值。
const double eps = 1e-5;
double f(double x){
return x*x;
}
double calSqrt(){
double l = 1,r = 2;
double mid;
while(r-l>eps){
mid = (l+r)/2;
if(f(mid)>2)
r = mid;
else
l = mid;
}
return mid;
}