二分查找法有两种解法,可以选择 左闭右闭[left,right] 和左闭右开[left,right) ,本文主要介绍左闭右闭方法,主要是我自己觉得这种方法比较好记(ㄒoㄒ)/~~
分析二分查找的一个技巧是:不要出现 else,而是把所有情况用 else if 写清楚,这样可以清楚地展现所有细节。
最基本的二分查找算法
- 我们初始化时:
left = 0,right = nums.length-1 - 这决定了我们的区间是
[left,right] - 所以循环的条件为:
left<=right while(left <= right)的终止条件是left == right + 1,写成区间的形式就是[right + 1, right]mid = Math.floor(left + (right-left)/2),- 这相当于
(right+left)/2,这样写能保证不会出现left+right相加太大导致溢出的情况,还能一直向下取整 - 同时:
left = mid+1,right = mid-1 - 因为我们只需要寻找一个
target,则找到nums[mid] == target时,返回mid即可
var search = function(nums, target) {
let left = 0
let right = nums.length-1
while(left<=right){
let mid = Math.floor(left + (right - left)/2)
if(nums[mid] == target){
return mid
}else if(nums[mid] < target){
left = mid+1 //边界[mid+1,right]
}else if(nums[mid] > target){
right = mid-1 //边界[left,mid-1]
}
}
return -1
};
寻找左侧边界
基本思路
在最基本的二分查找算法基础上更改
最关键的一步在于
if(nums[mid] == target){
right = mid-1 //收缩右侧边界
}
可见,找到 target 时不要立即返回,而是缩小「搜索区间」的上界right,在区间[left, mid-1]中继续搜索,即不断向左收缩,达到锁定左侧边界的目的
举例说明:寻找下面数组中2的第一个位置
[1,2,2,2,4]
第一次mid==2,找到目标值2,这时将右边界更新,区间变为[1,2]
第二次mid==0,小于目标值2,这时更新左边界,left=mid+1,这时left=right,区间只剩2
第三次mid==1,找到目标值2,右边界更新:right=mid-1,right==0,这时的left==1;满足跳出循环的套件left==right+1,跳出循环
最后直接返回left,即找到目标值2的左侧边界1
处理边界情况
由于 while 的退出条件是left == right + 1,所以当target比nums中所有元素都大时,会存在越界情况[1,0]这种区间,所以最后返回值要加上这种情况if (left >= nums.length || nums[left] != target) return -1
代码示例
var search = function(nums, target) {
let left = 0
let right = nums.length-1
while(left<=right){
let mid = Math.floor(left + (right - left)/2)
if(nums[mid] == target){
right = mid-1 //收缩右侧边界
}else if(nums[mid] < target){
left = mid+1
}else if(nums[mid] > target){
right = mid-1
}
}
// 检查出界情况
if (left >= nums.length || nums[left] != target) return -1
return left
};
寻找右侧边界
基本思路
与寻找左侧边界相同的逻辑,只不过缩小搜索区间,不断向右收缩
if (nums[mid] == target) {
left = mid + 1;
当nums[mid] == target时,不要立即返回,而是增大「搜索区间」的下界left,使得区间不断向右收缩,达到锁定右侧边界的目的。
处理边界情况
当target比所有元素都小时,right会被减到 -1,所以需要在最后防止越界
if (right < 0 || nums[right] != target) return -1;
代码示例
var search = function(nums, target) {
let left = 0
let right = nums.length-1
while(left<=right){
let mid = Math.floor(left + (right - left)/2)
if(nums[mid] == target){
left = mid+1 //只有这里需要改
}else if(nums[mid] < target){
left = mid+1
}else if(nums[mid] > target){
right = mid-1
}
}
// 这里改为检查 right 越界的情况
if (right < 0 || nums[right] != target) return -1;
return right;
};
寻找左右边界例题
//2次二分查找,查找 target 的左右边界,如果都找到,返回答案,否则,返回 -1
var searchRange = function(nums, target) {
let res = [-1,-1]
let left = 0
let right = nums.length-1
//寻找左边界
while(left<=right){
let mid = Math.floor(left + (right-left)/2)
if(target == nums[mid]){
right = mid-1//不断收缩右边界
}else if(target > nums[mid]){
left = mid+1
}else if(target < nums[mid]){
right = mid-1
}
}
if(left>=nums.length || nums[left] != target) return res
res[0] = left
//寻找右边界
right = nums.length-1
while(left<=right){
let mid = Math.floor(left + (right-left)/2)
if(target == nums[mid]){
left = mid+1//不断收缩左边界
}else if(target > nums[mid]){
left = mid+1
}else if(target < nums[mid]){
right = mid-1
}
}
if(right<0 || nums[right] != target) return res
res[1] = right
return res
};
模板整理自:labuladong二分查找,非常感谢老师~