变体一:查找第一个值等于给定值的元素
int bSearch(vector<int> &vec, int n, int value){
int beg = 0;
int end = n - 1;
while(beg <= end){
int mid = beg + ((end - beg)>>1);
if(vec[mid] > value) end = mid -1;
else if(vec[mid] < value) beg = mid + 1;
else {
if(mid == 0 || vec[mid - 1] != value) return mid;
else end = mid - 1;
}
}
return -1;
}
变体二:查找最后一个值等于给定值的元素
int bSearch(vector<int> &vec, int n, int value){
int beg = 0;
int end = n - 1;
while(beg <= end){
int mid = beg + ((end - beg)>>1);
if(vec[mid] > value) end = mid -1;
else if(vec[mid] < value) beg = mid + 1;
else {
if(mid == n-1 || vec[mid + 1] != value) return mid;
else beg = mid + 1;
}
}
return -1;
}
变体三:查找第一个大于等于给定值的元素
int bSearch(vector<int> &vec, int n, int value){
int beg = 0;
int end = n - 1;
while(beg <= end){
int mid = beg + ((end - beg)>>1);
if(vec[mid] >= value){
if(mid == 0 || vec[mid-1] < value) return mid;
else end = mid-1;
}
else
beg = mid + 1;
}
return -1;
}
变体四:查找最后一个小于等于给定值的元素
int bSearch(vector<int> &vec, int n, int value){
int beg = 0;
int end = n - 1;
while(beg <= end){
int mid = beg + ((end - beg)>>1);
if(vec[mid] <= value){
if(mid == n-1 || vec[mid+1] > value) return mid;
else beg = mid+1;
}
else
end = mid - 1;
}
return -1;
}
解答开篇
如何快速定位出一个 IP 地址的归属地?
如果 IP 区间与归属地的对应关系不经常更新,可以先预处理这 12 万条数据,让其按照**起始 IP **从小到大排序。如何来排序呢?IP 地址可以转化为 32 位的整型数。所以,我们可以将起始地址,按照对应的整型值的大小关系,从小到大进行排序。
然后,这个问题就可以转化为第四种变形问题“在有序数组中,查找最后一个小于等于某个给定值的元素”了。
当要查询某个 IP 归属地时,可以先通过二分查找,找到最后一个起始 IP 小于等于这个 IP 的 IP 区间,然后,检查这个 IP 是否在这个 IP 区间内,如果在,取出对应的归属地显示;如果不在,就返回未查找到。
IP地址与无符号整数的转换_qq_32523711的博客-CSDN博客
百度面试题------将一个电分十进制的IP地址转换为一个32位的整数,要求一一对应
内容小结
上一节我说过,凡是用二分查找能解决的,绝大部分我们更倾向于用散列表或者二叉查找树。即便是二分查找在内存使用上更节省,但是毕竟内存如此紧缺的情况并不多。
实际上,上一节讲的求“值等于给定值”的二分查找确实不怎么会被用到,二分查找更适合用在“近似”查找问题,在这类问题上,二分查找的优势更加明显。
比如今天讲的几种变体问题,用其他数据结构,比如散列表、二叉树,比较难实现。变体的二分查找算法写起来非常烧脑,很容易因为细节处理不好而产生 Bug,这些容易出错的细节有:终止条件、区间上下界更新方法、返回值选择。
课后思考
我们今天讲的都是非常规的二分查找问题,今天的思考题也是一个非常规的二分查找问题。如果有序数组是一个循环有序数组,比如 4,5,6,1,2,3。针对这种情况,如何实现一个求“值等于给定值”的二分查找算法呢?