二分的基础用法是在单调序列或单调函数中进行查找
本文章暂时只讨论整数集合上的二分查找
目录
一.两种形式的写法
以单调递增序列为例,有两种情况:
1.在序列a中查找≥x的数中的最小的一个;
2.在序列a中查找≤x的数中的最小的一个;
值得注意的是,二分法并不需要考虑这个序列的个数是奇数还是偶数
情况1:在序列a中查找≥x的数中的最小的一个
#include<stdio.h>
int main()
{
int a[7]={1,3,5,7,9,13,17};
int x; //x为我们要查找的条件范围
int l,r; //[l,r]为二分查找的区间
int mid; //二分的中间值
scanf("%d",&x);
//开始查找
while(l<r) //循环结束的条件是l=r,最后我们查找的数就是在l(或者是r)这个位置
{
int mid=(l+r)>>1;
//位运算,相当于除以2再向下取整 *为什么这里用位运算而不是直接用除法呢?
if(a[mid]>=x) r=mid; //如果中间的数大于等于我们要找的范围,那么我们只要前一半段
else l=mid+1; //如果中间的数小于我们要找的范围,那么我们只要后面一半段
}
//输出结果
printf("%d\n",a[l]);
return 0;
}
情况2:在序列a中查找≤x的数中的最大的一个
#include<stdio.h>
int main()
{
int a[7]={1,3,5,7,9,13,17};
int x;
int l,r;
int mid;
scanf("%d",&x);
//开始查找
while(l<r)
{
int mid=(l+r+1)>>1;
if(a[mid]<=x) l=mid;
else r=mid-1;
}
//输出结果
printf("%d\n",a[l]);
return 0;
}
两种情况的区别是:缩小范围时所取的mid,l,r有所不同:
1.r=mid,l=mid+1,mid=(l+r)>>1
2.l=mid,r=mid-1,mia=(l+r+1)>>1
二.为什么使用右移运算而不是整数除法
使用整数除法的话,在二分值域包含负数时不能正常工作
而右移运算不会存在这种问题
三.如何处理无解的情况
把a数组的一个越界的下标包含进来,如果最后二分终止在扩大后的这个越界下标上,则说明a中不存在所求的这个数;
以情况1( 在序列a中查找≥x的数中的最小的一个 )为例子
我们把它扩大到[1,n+1]
#include<stdio.h>
int main()
{
int a[8]={1,3,5,7,9,13,17};
int x;
int l,r;
int mid;
scanf("%d",&x);
//开始查找
while(l<r)
{
int mid=(l+r)>>1;
if(a[mid]>=x) r=mid;
else l=mid+1;
}
//输出结果
if(l=7) printf("No Found\n");
else printf("%d\n",a[l]);
return 0;
}
对于情况2,我们把范围扩大到[0,n]