js二分查找算法模板

307 阅读2分钟

二分查找法有两种解法,可以选择 左闭右闭[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即可

力扣704:二分查找

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-1right==0,这时的left==1;满足跳出循环的套件left==right+1,跳出循环
最后直接返回left,即找到目标值2的左侧边界1

处理边界情况

由于 while 的退出条件是left == right + 1,所以当targetnums中所有元素都大时,会存在越界情况[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;
};

寻找左右边界例题

力扣34. 在排序数组中查找元素的第一个和最后一个位置

//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二分查找,非常感谢老师~