二分查找常用与一个有序序列或一个具有单调性问题中。
8.1 编程思路:
- 设有一数组 (a[n]) ,数组中的元素按值从小到大排列有序。用变量 (low, high) 和 (mid) 分别指示待查元素所在区间的下界、上界和中间位置。初始时,(low=0,high=n-1) ,查找 (x) 是否在序列中。
- 令 (mid = (low+ high) /2) 。(建议写成 (mid = low+(high-low)/2),可以避免数过大越界)
- 比较给定值 (x) 与 (a[mid]) 值的大小:
- 若 (a[mid] == x) ,则查找成功,结束查找;
- 若 (a[mid]> x) ,则表明给定值 (x) 只可能在区间 (low sim mid-1) 内,修改检索范围。令 (high=mid-1),(low) 值保持不变;
- 若 (a[mid]< x) ,则表明给定值 (x) 只可能在区间 (mid+1sim high) 内,修改检索范围。令 (low=mid+1,high) 值保持不变。
- 比较当前变量 (low) 和 (high) 的值,若 (low≤high) ,重复执行第1、2两步,若 (low>high) ,表明数组中不存在待查找的元素,查找失败。
- eg : a[]={10,14,21,38,45,47,53,81,87,99},查找47
8.2 代码实现:
int binarysearch(int a[],int num,int x){
int left=0,right=num-1;//查找区间[left,right]
while(left<=right){//跳出循环时left=right+1
int mid = left+(right-left)/2;//避免(left+right)溢出
if(a[mid] == x)return mid;
if(a[mid] < x)
left = mid + 1;
else
right = mid - 1;
}
return -1;//执行到这一步说明个序列中不存在x
}
- 上面算法在查找一个数是否存在很高效,但存在局限性。
- 比如说给你有序数组$a[]={1,2,2,2,3}, x=2$,此算法返回的索引是 $2$,没错。但是如果我想得到 (x) 的左侧边界,即索引 $1$,或者我想得到 (x) 的右侧边界,即索引 $3$ ,那我们该如何处理。
8.2.1 左侧边界代码实现
int binarysearch(int a[],int num,int x){//查找数组a,[0,num-1]中比x小的个数
int left=0,right=num;//查找区间[left,right)
while(left<right){//跳出循环时left=right
int mid = left+(right-left)/2;//避免(left+right)溢出
if(a[mid] >= x) //求左边界我们尽量遇到==x的时候右边界往左移
right = mid;//注意是左闭右开区间右区间缩小不需要减一
else
left = mid + 1;//新的区间不包含a[mid]
}
return left;//跳出循环时left指向第一个x的位置
}
8.2.2 右侧边界代码实现
int binarysearch(int a[],int l,int r,int x){//查找数组a,[l,r]中元素x的位置
int left=0,right=num;//查找区间[left,right)
while(left<right){//跳出循环时left=right
int mid = left+(right-left)/2;//避免(left+right)溢出
if(a[mid] <= x) //求右边界我们尽量遇到==x的时候左边界往右移
left = mid+1;//注意是左闭右开区间
else
right = mid;//新的区间不包含a[mid]
}
return left-1;//注意left要减一,跳出循环时left指向最右x的下一个位置
}