算法基础03 二分法

二分查找的基本问题

二分查找是一种算法,其输入必须是有序的元素列表,对于包含n个元素的列表,使用二分查找最多需要$\log_2^n$

对数运算是幂运算的逆运算

log_2^n = a  →  2^a=n

在使用大O表示法讨论运行时间时,$\log$指的都是$\log_2$(以2为底)

二分法的核心思想是逐渐缩小问题规模,在练习和学习的时候,要着眼于掌握算法的思想,而不该去纠结二分的几种写法的区别和细节

二分查找的基本问题在LeetCode上是704题,二分查找

var search = function (nums, target) {
  let left = 0,
    right = nums.length - 1;
  while (left <= right) {
    const mid = ~~((left + right) / 2);
    if (nums[mid] === target) {
      return mid;
    }
    if (nums[mid] > target) {
      right = mid - 1;
    } else {
      left = mid + 1;
    }
  }
  return -1;
};

二分查找的思路就是根据待搜索区间的中间元素nums[mid]target的值的大小关系,判断下一轮搜索需要在哪个空间进行查找,进而设置leftright的值,分为三种情况

  1. nums[mid] === target,找到了目标元素,直接返回
  2. nums[mid] > target,说明mid以及mid右边的所有元素,一定比target大,下一轮搜索的区间改为[left, mid - 1],因此需要设置right = mid - 1
  3. nums[mid] < target,与上一条相反,下一轮搜索的区间改为[mid + 1, right],因此需要设置left = mid + 1

如果while循环结束,函数仍未退出,说明不存在目标元素,返回-1

二分查找的问题变种

大多数二分查找的题目都不是这么简单,一般不是找与target相等的值,比如找到小于等于target的下标最小的元素,这样的题目的特点是,如果nums[mid]等于target时,还需要继续查找

可以使用如下的思路和模板进行思考:

区间划分和缩小

根据中间位置元素的值nums[mid]把待搜索区域分为两个部分:

  • 一定不存在目标元素的区间,下一轮搜索不需要考虑
  • 可能存在目标元素的区间,下一轮搜索需要考虑

mid只可能存在于这两个区间的一个,即while里面的if...else有两种写法:

  • 如果mid被分到左边区间,区间被分为[left, mid][mid + 1, right],此时分别设置right = midleft = mid + 1
  • 如果mid被分到右边区间,区间被分为[left, mid - 1][mid, right],此时分别设置right = mid - 1left = mid

循环条件

while的循环条件写成left < right,在上面把待搜索区间分为两个部分的情况下,退出循环一定left === right,这样就不需要考虑最终返回left还是right的问题了

if...else的条件

把容易想到的、不容易出错的逻辑写在if里面,什么是容易想到、不容易出错的逻辑呢,题目要求我们需要符合条件A的元素,那么我们就将条件A的反面作为if的逻辑

练习

以LeetCode的35题搜索插入位置为例,实际上,题目要求的是找到第一个大于等于target的下标,那么我们if的条件去取反,那么就是nums[mid] < target

在这个条件下,区间缩小的规则就是left = mid + 1,与它配套的反面空间就是right = mid,所以就可以写出代码了:

var searchInsert = function (nums, target) {
  const length = nums.length;

  // right 初始值取的是 length,而不是 length - 1,这样 left 就可以去到最后一个有效元素 length- 1
  // 如果 right 初始值是 length - 1,需要针对插入到最后的情况进行特殊处理
  let left = 0,
    right = length;

  while (left < right) {
    const mid = ~~((left + right) / 2);

    if (nums[mid] < target) {
      // 下一轮搜索空间 [mid + 1, right]
      left = mid + 1;
    } else {
      // 下一轮搜索空间 [left, mid]
      right = mid;
    }
  }

  return left;
};

总结

  1. 想清楚题目是否可以使用二分法,题目中包含单调性或者可以缩减问题规模的特点,常见的应用有在有序或者半有序的数组中找下表,确定一个有范围的整数
  2. 确定搜索范围
  3. 从『中间元素什么时候不是解』开始反向思考if的条件
  4. 把区间分为两个部分,while循环的条件不包含等号
  5. 根据mid所在区域,缩小问题规模
  6. 考虑right的取值是不是要增加一位
  7. 如果遇到left = mid的情况,mid需要向上取值

二分查找系列专题

简单难度

中等难度

参考

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值