前言
二分查找法用了这么久,总结一下常见的变种,这里看了很多别人的文章,在这里把它们说的总结一下(其实维基百科里面写的就非常全面了,不仅提出了问题,还有关于二叉查找的一些变种,链接可以在下面的介绍里面点击英文)
什么是二分查找
二分查找(Binary search)也称折半查找,它是一种效率较高的查找方法
算法前提
- 线性表必须采用顺序存储结构
- 表中元素按关键字有序排列
算法复杂度
采用分治策略,可在最坏的情况下用 O(log n)
完成搜索任务
经典的二分查找法
伪代码
function binary_search(A, n, T):
L := 0
R := n − 1
while L <= R:
m := floor((L + R) / 2)
if A[m] < T:
L := m + 1
else if A[m] > T:
R := m - 1
else:
return m
return unsuccessful
溢出问题
在你的写法里面, mid
可能是是这样算出来的:
int mid = (l + r) / 2;
这一个操作很可能会造成溢出,有一个很简单的改进方法:
int mid = l + (r - l) / 2;
这样就能保证不会出现溢出的问题
C / C++
int binary_search(int *array, int size, int key){
int l = 0;
int r = size - 1;
while(l <= r){
int mid = l + (r - l) / 2;
if(array[mid] < key) l = mid + 1;
else if(array[mid] > key) r = mid - 1;
else return mid;
}
return -1;
}
二分查找的变种
第一类:查找和目标值相等的数
这个就是最经典的二分查找的目的,没什么好说的
int binary_search(int *array, int size, int key){
int l = 0;
int r = size - 1;
while(l <= r){
int mid = l + (r - l) / 2;
if(array[mid] < key) l = mid + 1;
else if(array[mid] > key) r = mid - 1;
else return mid;
}
return -1;
}
第二类:查找第一个不小于目标值的数/查找最后一个小于目标值的数
查找第一个不小于目标值的数(第一个 >= 目标值) 对应C++的STL的函数 lower_bound
注意可能会存在下面的情况
- 目标值不一定会在数组中出现
- 与目标值相等的数在数组中并不唯一,即有多个的情况
int binary_search_lower_bound(int *array, int size, int key){
int l = 0;
int r = size - 1;
while(l <= r){
int mid = l + (r - l) / 2;
if(array[mid] < key) l = mid + 1;
else r = mid - 1;
}
return r + 1;
}
它可以变形为查找最后一个小于目标值的数(最后一个 < 目标值),其实就是上面的往左移一位
{
...
return r;
}
第三类:查找第一个大于目标值的数/查找最后一个不大于目标值的数
查找第一个大于目标值的数(第一个 > 目标值) 这类变种对应C++的STL的函数 upper_bound
与第二类相似,同时也可能出现下面的情况
- 目标值不一定会在数组中出现
- 与目标值相等的数在数组中并不唯一,即有多个的情况
int binary_search_upper_bound(int *array, int size, int key){
int l = 0;
int r = size - 1;
while(l <= r){
int mid = l + (r - l) / 2;
if(array[mid] <= key) l = mid + 1;
else r = mid - 1;
}
return r + 1;
}
它可以变形为查找最后一个不大于目标值的数(最后一个 <= 目标值),其实就是上面的往左移一位
{
...
return r;
}
其他
待补充
参考文章
[1] 二分搜索法小结: http://www.cnblogs.com/grandyang/p/6854825.html
[2] 你真的会写二分查找吗:https://www.cnblogs.com/luoxn28/p/5767571.html