手把手带你撕出正确的二分法 || Leetcode704

240 阅读3分钟

原理

二分法查找的原理基于有序数组的特性。

它通过不断将数组分割成两部分,反复比较中间元素与目标值的大小关系,从而逐步缩小查找范围。

具体来说:

  1. 确定数组的左右边界,一般设左边界为 0,右边界为数组长度减 1。
  2. 计算中间索引 mid,将中间元素与目标值进行比较。
  3. 如果中间元素等于目标值,直接返回中间索引。
  4. 如果中间元素小于目标值,则在右半部分继续查找,更新左边界为 mid + 1。
  5. 如果中间元素大于目标值,则在左半部分继续查找,更新右边界为 mid - 1。
  6. 重复步骤 2 至步骤 5,直到找到目标值或确定目标值不存在。

这种方法的时间复杂度为 O(log n),相比于顺序查找,效率更高。它的优势在于通过不断缩小查找范围,快速定位目标元素。

LeetCode704实操

这是二分查找一道最经典的案例。

题目

给定一个 n 个元素有序的(升序)整型数组 nums 和一个目标值 target  ,写一个函数搜索 nums 中的 target,如果目标值存在返回下标,否则返回 -1

示例

示例 1:

输入: nums = [-1,0,3,5,9,12], target = 9
输出: 4
解释: 9 出现在 nums 中并且下标为 4

示例 2:

输入: nums = [-1,0,3,5,9,12], target = 2
输出: -1
解释: 2 不存在 nums 中因此返回 -1

解题

/**
 * @param {number[]} nums
 * @param {number} target
 * @return {number}
 */
var search = function(nums, target) {
    var left = 0;
    var right = nums.length-1;
    
    while(left <= right){
        var mid = Math.floor(left + (right - left)/2);
        if(nums[mid] < target){
            left = mid + 1;
        }else if(nums[mid] > target){
            right = mid - 1;
        }else {
            return mid;
        }
        
    }
    return -1;
};

需要注意的点

  1. js不会和java一样自动取整,这里需要我们手动向下取整
  2. mid值正常写法:(左边加右边)除以2,但是如果值非常大,可能会存在溢出的情况,因此改写为左边+(右边-左边)/2,防止溢出,通分后和正常写法是一样的
  3. 事实上二分法有两种写法,很多小白朋友不知道要不要加等号,事实上我们只要保持一致性原则就不会出错,当我们while中写的是左边小于等于右边(左闭右闭),那么在我们拿目标值与中间值比较时,中间值已经小于或者大于目标值了,那么接下来的区间就不应该包含这个中间值,就需要+1/-1,而如果我们while中的写法是只小于,没有包含等于的情况(左闭右开),那么当目标值小于中间值时,目标区域在左边,更新右边界,right应该等于mid,因为原来就没有把这个值算在里面,而目标值大于中间值时,目标查询区域在右边,更新左边界,依旧是左边=mid+1,(左闭右开),因此我们时刻保持一致性原则就不会出错。