【算法】二分查找详解

算法介绍

如果需要在一个有序数组中查找某一特定元素,按照常规思路,我们会从头开始遍历一下这个数组,直到找到这个元素。显然,这个过程的算法时间复杂度为 O ( n ) O(n) O(n),当数据范围比较大时,就有可能超时。本文所讲的二分查找就能将该问题的时间复杂度降为 O ( l o g n ) O(logn) O(logn)其大致过程为每次拿数组中间的值与目标值进行比较,如果数组中间的值比目标值大(假定有序数组顺序为从小到大),说明目标值在数组左边,否则在右边。

算法模板

二分的本质是边界,多用于具有单调性的题目中。如果我们在一个区间上定义某种性质,使得整个区间可以被一分为二,即这个性质在一边区间满足而在另一边区间不满足。那么使用二分就可以找出左边界的右端点或右边界的左端点。
可能你会发现网上二分查找的算法介绍特别多,一般文章中都会提到边界问题,这也是二分查找比较难的地方。因此,下面给出二分查找通用的模板,无需考虑边界问题。
注意:题目可能无解,但二分一定有解。

  1. 寻找右边界的左端点,区间 [ l , r ] [l, r] [l,r] 被划分成 [ l , m i d ] [l, mid] [l,mid] [ m i d + 1 , r ] [mid + 1, r] [mid+1,r] 时使用:
function binarySearch(l, r) {
  while (l < r) {
    let mid = l + r >> 1;
    if (check(mid)) r = mid; // 满足所定义的性质
    else l = mid + 1;
  }
  return l;
}
  1. 寻找左边界的右端点,区间 [ l , r ] [l, r] [l,r] 被划分成 [ l , m i d − 1 ] [l, mid - 1] [l,mid1] [ m i d , r ] [mid, r] [mid,r] 时使用:
function binarySearch(l, r) {
  while (l < r) {
  	// 加 1 是为了防止当 r = l + 1 时 (l + (l + 1)) / 2 = l 进入 [l, l + 1] 死循环
    let mid = l + r + 1 >> 1; // 向上取整
    if (check(mid)) l = mid; // 满足所定义的性质
    else r = mid - 1;
  }
  return l;
}

实战

  1. LeetCode704. 二分查找
    题意:在有序数组 nums (元素不重复)中找 target ,返回下标,不存在则返回 -1
    解法:我们定义一个性质:nums[mid] >= target,则数组被分为左右两个区间,左边的数都小于 target,右边的数都大于等于 target。我们要寻找的解就是右边区间的左端点,因此,采用第一个模板。
/**
 * @param {number[]} nums
 * @param {number} target
 * @return {number}
 */
var search = function(nums, target) {
    let l = 0, r = nums.length - 1;
    while (l < r) {
        let mid = l + r >> 1;
        if (nums[mid] >= target) r = mid;
        else l = mid + 1;
    }
    if (nums[l] === target) return l;
    else return -1;
};
  1. LeetCode34. 在排序数组中查找元素的第一个和最后一个位置
    该题也是模板题,代码如下:
/**
 * @param {number[]} nums
 * @param {number} target
 * @return {number[]}
 */
var searchRange = function(nums, target) {
  let res = [];
  let l = 0, r = nums.length - 1;
  while (l < r) {
    let mid = l + r >> 1;
    if (nums[mid] >= target) r = mid;
    else l = mid + 1;
  }
  if (nums[l] != target) return [-1, -1];
  else {
    res.push(l);
    l = 0, r = nums.length - 1;
    while (l < r) {
      let mid = l + r + 1 >> 1;
      if (nums[mid] <= target) l = mid;
      else r = mid -  1;
    }
  }
  res.push(l);
  return res;
};
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值