一、简介:
1、何为二分?
二分查找也称折半查找(Binary Search),它是一种效率较高的查找方法。但是,折半查找要求线性表必须采用顺序存储结构,而且表中元素按关键字有序排列。
2、怎样能二分?
- 是一个有序数列(俗称符合单调性)
- 必须是按照关键字大小进行排序
3、如何二分?
1.思路:
假设我们比较的有序数列有三个数,我们比较一个元素的值和数组中间位置的元素的值进行比较,如果比中间的元素大,则在有序数组的后半部分进行查找;如果中间位置的元素的值小,则跟有序数组的前半部分进行比较;如果相等,则找到了比较元素的位置.
2.过程:
(1)设置查找区间:
int l=1.r=n;
(2)判断是否成立,否则查找失败,返回-1,是则继续执行(3);
(3)取中间mid=l+r>>1,比较a[mid]与x(要找的数),得以下3情况:
1. 若 x < a[mid],则right = mid - 1;查找在左半区间进行,转(2);
2. 若 x > a[mid],则left = mid + 1;查找在右半区间进行,转(2);
3. 若 x = a[mid],则查找成功,返回mid的值;
3.实现(递归与循环):
int ef(int l, int r, int x) {
if(l>=r) return -1;
int mid=l+r>>1;
if (x<a[mid]) {
return ef(l,mid-1,x);
}
else if (x>a[mid]) {
return ef(mid+1,r,x);
}
else {
return mid;
}
}
int ef(int l,int r,int x){
while(l<r){
int mid=l+r>>1;
if(a[mid]<x) l=mid+1;
else if(a[mid]>x) r=mid-1;
else return mid;
}
return -1;
}
二分之重,在于 l 和 r 还有 mid 在每一次比较之后应该怎样的变 。
4.图示
二分简直是遥遥领先啊。
二、例题:
何老板的淘宝店http://oi.nks.edu.cn:19360/zh/Problem/Details?cid=2772&tid=A
若数据加强,用桶定然会MLE,故这题应该用二分,先将商品价格由小到大排序,再在数组中用二分查找每位顾客要买的商品有没有。若有,则将ans加上数量乘单价。
核心代码:
bool erfen(long long l,long long r,long long x){
long long mid=(r+l)/2;
while(l<=r){
mid=(r+l)/2;
if(a[mid]==x){
return true;
}
else if (a[mid]<x) {
l=mid+1;
}
else if(a[mid]>x){
r=mid-1;
}
}
return false;
}
人数统计http://oi.nks.edu.cn:19360/zh/Problem/Details?cid=2772&tid=B
对于每个a[i](排序后),查找最后一个小于等于他的数(下标)。紧接着判断他与要找的数是否相同,若不相同,则说明序列中并没有此元素,若相同,则查找最后一个小于等于他+1的数(下标)(也就是第一个大于等于他的数),将两数组下标相减,可得k的个数。另外,如果输入数字不在查找范围之内,直接输出0即可。
核心代码:
for(int i=1;i<=m;i++){
scanf("%d",&x);
if(x<a[1]){
printf("0\n");
continue;
}
if(x>a[n]){
printf("0\n");
continue;
}
cnt_l=rf(x,n);
if(a[cnt_l]!=x){
printf("0\n");
continue;
}
if(x==a[n]){
a[n+1]=x+1;
cnt_r=rf(x+1,n+1);
}
else
cnt_r=rf(x+1,n);
printf("%d\n",cnt_r-cnt_l);
a[n+1]=0;
}
三、总结
私以为,二分的金随,就在于“边界”。l是等于mid+1还是mid?r是等于mid-1还是mid?a[mid]于x的比较是否取等?l是<r还是<=r?
综上所述,我们可以将模板背过。
int rf(int m,int y){
l=1,r=y;
while(l<r){
int k=(l+r)/2;
if(a[k]<m){
l=k+1;
}
else r=k;
}
return l;
}