一、查找精确值
从一个有序数组中找到一个符合要求的精确值(如猜数游戏)。如查找值为Key的元素下标,不存在返回-1。
//这里是left<=right。
//考虑这种情况:如果最后剩下A[i]和A[i+1](这也是最容易导致导致死循环的情况)首先mid = i,
//如果A[mid] < key,那么left = mid+1 = i +1,如果是小于号,则A[i + 1]不会被检查,导致错误
int left = 1,right = n;
while(left <= right)
{
//这里left和right代表的是数组下标,所有没有必要改写成mid = left + (right - left)/2;
//因为当代表数组下标的时候,在数值越界之前,内存可能就已经越界了
//如果left和right代表的是一个整数,就有必要使用后面一种写法防止整数越界
int mid = (left + right) / 2;
if(A[mid] == key)
return mid;
else if(A[mid] > key)//这里因为mid不可能是答案了,所以搜索范围都需要将mid排除
right = mid - 1;
else
left = mid + 1;
}
return -1;
二、查找符合条件的位置
1.查找第一个≥key的元素的位置
设a为单调不减整数系列,在系列a中查找第一个≥key的数的位置。
int left=1,right=n+1;//为什么right的初始值为n+1,因为存在着key比所有元素都大的情况
int lower_bound(int a[],int left,int right,int key){
int mid;
while(left<right){
mid=(left+right)/2;
if(a[mid]>=key) right=mid;
else left=mid+1;
}
return left;
}
2.查找最后一个≤key的元素的位置
设系列a为单调不减整数系列,在系列a中查找最后一个≤key的数的位置。
int left=0,right=n;//left和right边界初始区间为[0,n],因为key有可能比所有的数都更小
int last_bound(int a[],int left,int right,int key){
int mid;
while(left<right){
mid=(left+right+1)/2;//为什么还要多加1呢??
if(a[mid]<=key) left=mid;
else right=mid-1;
}
return left;
}
第二种模板比较复杂,它涉及到两种缩小区间的形式。
形式一:right=mid,left=mid+1,取中间值时mid=(left+right)/2;
形式二:left=mid,right=mid-1,取中间值时mid=(left+right+1)/2;
三、总结
为什么第二种查找的循环跳出条件是left<right,为什么不是≤呢?因为我们的区间变换思路是不断的舍去不可能是解的区间,最后只剩下一个数就是我们的解。而第一种情况就算最后只剩一个数也有可能不是解,所以需要使用小于等于。
- 查找精确值,循环条件是小于等于;查找满足情况的最大最小值,循环条件是小于。
- 查找满足条件的最大数,mid = (right + left + 1) / 2;查找满足条件的最小数,mid = (right + left)/2
- mid = left + (right - left) / 2,不是适用于所有的情况。
作者:T-star
来源链接:https://www.acwing.com/blog/content/307/