问题描述:给定一个排序的整数数组(升序)和一个要查找的整数target
,用O(logn)
的时间查找到target第一次出现的下标(从0开始),如果target不存在于数组中,返回-1
。
问题说明:要注意数组中会出现重复数字,第一次找到的值未必是最后的答案。
问题分析:二分查找是分治算法中十分经典的问题,网上也有很多实现的代码,JS也有很多。但网上给出的代码大部分并没有考虑到重复值的问题,算法只能用于没有重复值的数组,这样算法的应用型就很低。在这里我改进了算法,可以在有重复值的情况下运行,若大家有进一步改进的算法,欢迎留言指正~
基本的二分查找实现起来很简单,我们定义两个指针,low和high,分别从数组的头部和尾部开始。定义一个变量mid,用来存放low和high的中间值。注意!JS的除法保留小数,所以还要记得取整。
若中间值较大,则把mid-1赋给high进入新循环;若中间值较小,就把mid+1赋给low进入新循环。
下面,最关键的就是当mid对应的值与target相等的情况。若没有重复值,可以直接返回mid即可。但当存在重复值的时候,意味着我们找到的值的前方还可能有相同的值。
此刻,我们把mid赋给high。注意,是mid而不是mid-1了,因为当前mid指向的值本身已经是相等了,所以不能减1。至于为什么要把值赋给high而不是low,是因为我们要找的是target第一次出现从下标,所以若当前mid不是所求,那么我们找到值只可能在前面而不是后面,所以把值赋给high。
这样直到low和high(包括low和high)之间的数字只有两个或者一个时,我们开始判断。首先判断low指向的值,若相等,则返回;若不相等,则返回high的值。
代码实现:
const binarySearch = function (nums, target) {
var low = 0;
var high = nums.length-1;
while(low <= high){
var mid = parseInt((low + high)/2);
if(nums[mid] == target){
if((high-low)>1){
high = mid;
}else if(nums[low] == target){
return low;
}else if(nums[high] == target){
return high;
}
}else if(nums[mid] > target){
high = mid - 1;
}else{
low = mid + 1;
}
}
return -1;
}