二分搜索的写法
二分搜索框架
int L,R,key;
while(L<=R)
{
int mid=(L+R)>>1;
if(a[mid]<key or a[mid]<=key)
L=mid+1;
else
R=mid-1;
}
return L or R;
问题
二分搜索大家都很熟悉。如果待查区间内只有一个key值,要求找出key,那么一切都很容易。
但是如果有多个key,或者没有key,在不依赖现有库函数的情况下,怎么做到返回第一个key的位置,或最后一个key的位置,或者第一个大于key的位置此类的要求呢?
解决
其实很简单,只需要关注两个部分,一个是比较,另一个是返回值。
比较的控制
-
假设区间内有key,那么a[mid]<=key的写法会使得左端点一直向右移动。这样当L==R时,找到的就是最后一个key的位置。
同理,a[mid]<key的写法会使得右端点向左移动,当L==R时,找到的就是第一个key的位置。
-
若区间内没有key,那么两种写法是等价的,因为不存在a[mid]==key的情况。
返回值的问题
注意到,二分搜索结束的条件是L>R,那么最后的状态一定是L在R的右边,二者相邻。
-
如果有key,那么根据上文中所述的写法的不同,会有两种情况。
1.1 R指向最后一个key,L在R右侧。
1.2 L指向第一个key,R在L左侧。
-
如果没有key。那么只有一种情况,即L指向第一个大于key的元素,R指向最后一个小于key的元素。
至此问题就解决了,只需要控制这两部分,就可以实现任意一种二分搜索的变体。
示例
-
查找第一个大于key的元素位置(类似stl的upper_bound的功能)
int L,R,key; while(L<=R) { int mid=(L+R)>>1; if(a[mid]<=key) L=mid+1; else R=mid-1; } return L;
-
查找第一个key的位置,若不存在,则返回第一个大于key的元素位置(类似stl的lower_bound的功能)
int L,R,key; while(L<=R) { int mid=(L+R)>>1; if(a[mid]<key) L=mid+1; else R=mid-1; } return L;
-
查找最后一个小于等于key的元素的位置
int L,R,key; while(L<=R) { int mid=(L+R)>>1; if(a[mid]<=key) L=mid+1; else R=mid-1; } return R;