二分查找法及其边界查找
概述
二分查找法又称为折半查找,适合对已排序好的数据集合进行查找,时间复杂度为O(log2n)
思路
- 假设一个有序数据集合,找出其最中间的元素,以中间元素为界将集合划分为左右两个子集
- 将中间元素与目标元素进行比较,如果相等则是找到直接返回,如果大于/小于中间元素,则到左右子集中大于/小于中间元素的那一个里面去找
- 重复以上过程直到找到为止
- 如果某一步数组为空,则是找不到目标元素,就返回-1
代码(js)
- 递归
//升序数组的二分查找法 function binarySearch(key, arr, begin, end) { if(begin > end) //数组为空,找不到目标元素 return -1; var mid = (begin+end) >> 1; if(key == arr[mid]) return mid; //返回元素位置 else if(key < arr[mid]) //小于则去左数组找 return binarySearch(key, arr, begin, mid-1); else //大于则去右数组找 return binarySearch(key, arr, mid+1, end); }
- 非递归
//升序数组的二分查找法 function binarySearch(key, arr) { var begin = 0, end = arr.length - 1; while(begin <= end){ var mid = (begin+end) >> 1; if(key == arr[mid]) return mid; else if(key < arr[mid]) end = mid-1; else begin = mid+1; } return -1; }
局限性
比如有序数组 nums = [1,2,2,2,3],target = 2,此算法返回的索引是 2。但如果想得到 target 的左侧边界,即索引 1,或想得到 target 的右侧边界,即索引 3,这样的情况此算法无法处理
寻找左侧边界的二分查找
//升序数组
function binarySearch(key, arr)
{
var begin = 0
var end = arr.length; //注意点1
while(begin < end){ //注意点2
var mid = (begin+end) >> 1;
if(key == arr[mid])
end = mid; //注意点3
else if(key < arr[mid])
end = mid; //注意点4
else if(key > arr[mid])
begin = mid+1;
}
return arr[begin] == key ? begin : -1; //注意点5
}
注意点:
end = arr.length
,令搜索数组的区间变为[begin, end)
左闭右开- 令
while(begin < end)
,因为需要当begin == end
时则跳出循环,此时搜索区间为[begin, begin)
即为空 - 找到目标时不立刻返回,而是令
end = mid
,向左缩小搜索范围,查找中间元素左边是否有相同的元素 - 因为搜索区间为
[begin, end)
左闭右开,所以是end = mid
而不是end = mid-1
- 如果在数组[2,3,4]中查找1,此代码返回0,可以理解为数组中小于1的数有0个;同理,如果在数组[2,3,4]中查找5,此代码返回3,可以理解为数组中有3个数小于5。所以最后要判断一下
寻找右侧边界的二分查找
//升序数组
function binarySearch(key, arr)
{
var begin = 0
var end = arr.length;
while(begin < end){
var mid = (begin+end) >> 1;
if(key == arr[mid])
begin = mid+1; //注意点1
else if(key < arr[mid])
end = mid;
else if(key > arr[mid])
begin = mid+1;
}
return arr[begin-1] == key ? (begin-1) : -1; //注意点2
}
注意点:
- 找到目标时不立刻返回,而是令
begin = mid+1
,向右缩小搜索范围,查找中间元素右边是否有相同的元素 - 为什么是
begin-1
,因为注意点1中令begin = mid+1
,所以最后跳出循环时begin指向目标元素右边一位
参考:
① https://blog.csdn.net/u012194956/article/details/79103843
② https://www.cnblogs.com/kyoner/p/11080078.html