二分查找重难点总结(边界条件、循环条件、mid计算方法等)

1. 二分查找易错点总结

二分查找中有三个点需要特别注意:

  • 搜索范围的左右边界,即left = 0还是left = -1right = nums.lengh-1 还是right = nums.lengh
  • 搜索停止(循环结束)的条件,即while(left < right)while(left <= right)的选择问题;
  • 搜索时中间值能否加入左/右边界,即right = mid 还是right = mid-1left = mid还是left = mid+1
  • mid怎么计算,即mid = left + (right - left) / 2还是mid = left + (right - left + 1) / 2

注意:我在下面总结的是一个“系统”,如果按照下面的方法确定搜索边界,那么循环终止条件也必须按照下面的思路选择,不然很容易迷糊。其实不按照下面的思路,甚至是相悖的思路也能正确解题,只不过我太菜了所以才这样总结,希望大神勿喷。

1.1 搜索边界的确定

假设有序数组为int[] nums,有如下三种情况需要考虑:

  • 示例一
    我们要搜索的是大于target的最小值索引,而target比数组中所有的数都要大,那么[0, nums.lengh-1]内就没有满足条件的索引,右边界必须扩大,即令right = nums.lengh,此时目标索引就是 nums.lengh。而左边界不用动,因为即使target比数组中所有的数都要小,目标索引就是0了,所以搜索区间改为[0, nums.lengh]。可以结合下图思考。
    在这里插入图片描述

  • 示例二
    我们要搜索的是小于target的最大值索引,而target比数组中所有的数都要小,那么[0, nums.lengh-1]内就没有满足条件的索引,左边界必须扩大即令left = -1,此时目标索引就是-1。右边界不用动,因为即使target比数组中所有的数都要大,小于target的最大值索引就是nums.lengh-1,所以搜索区间为[-1, nums.lengh-1]。可以结合下图思考。
    在这里插入图片描述

  • 示例三(一般用于搜索区间内是否有某个值
    我们要搜索的是等于target的索引,数组中有可能有target,搜索区间为[0, nums.lengh-1],因为当在该区间内搜索不到时,会跳出循环,直接返回-1表示搜索不到就行。其实,若题目要求:如果target比数组中所有的数都大,返回nums.lengh,如果target比数组中所有的数都小,返回-1,此时就可以把区间扩充为[-1, nums.lengh],不过此时要注意循环结束条件。
    一般搜索边界都是令left = 0right = nums.lengh-1

1.2 循环结束条件

假设有序数组为int[] nums,求解的是索引,只需考虑两种情况:

  • 情况一
    如果搜索区间[left, right]中一定有目标值索引,那么循环截止条件是while(left < right),因为当left == right时目标索引就是left或者right,也就是说1中讨论的情况一和情况二循环终止条件都是while(left < right),甚至情况三时,如果我们将搜索区间扩充为[-1, nums.lengh],循环终止条件也是while(left < right)
  • 情况二
    如果搜索区间[left, right]中不一定有目标值索引,那么循环截止条件是while(left <= right);(一般用于搜索区间内是否有某个值

注意:
如果我们应根据题目要求选择是否扩充边界,如果按照1种的思路去扩充了,那么循环截止条件一定是while(left < right)

1.3 中间值归属问题

这个问题其实比较灵活,这里我只讨论3种情况,其余情况类似。假设有序数组为int[] nums,求解的是索引,根据题目要求分为3种情况:

  • 情况一
    假设我们要搜索的是大于6的最小值索引,如果num[mid] <= 6,那么这个mid一定不是目标索引,此时left = mid + 1(因为mid位置的值比目标值还小,而我们找的元素肯定大于目标值),如果如果num[mid] > 6,此时的mid是有可能是目标索引的,所以令right = mid
  • 情况二
    假设我们要搜索的是小于6的最大值索引,如果num[mid] >= 6,那么这个mid一定不是目标索引,此时right = mid - 1(因为mid位置的值比目标值还大,而我们找的元素肯定小于目标值),如果如果num[mid] < 6,此时的mid是有可能是目标索引的,所以令left = mid
  • 情况三(最简单)
    假设我们要搜索的是等于6的索引,如果num[mid] > 6,那么这个mid一定不是目标索引,此时right = mid - 1;如果如果num[mid] < 6,此时的mid也肯定不是目标索引,所以令left = mid + 1;如果num[mid] == 6,那就找到啦;

1.4 mid的计算方法问题

mid = left + (right - left) / 2还是mid = left + (right - left + 1) / 2是完全不同的,一个取得是靠左边的中位数,一个取得是靠右的中位数,具体用哪种计算方法呢?
先说结论,循环体中有令left = mid,一定选第二种,否则选第一种。所以敲代码时,一般先写上mid = left + (right - left) / 2,写完循环体再看看需不需要改。
下面说一下为什么是这样。如下图所示,如果有left = mid这个条件,就意味着左边界有可能是一直不动的,当出现图中left与right紧挨着的时候,mid会始终等于左边界,程序就会陷入死循环,这是编程语言语法导致的。所以为了避免这种情况,使得当left与right紧挨着时left也能向右滑动,必须令mid = left + (right - left + 1) / 2。而如果有right = mid这个条件则不必担心这个问题,因为编程语言语法自动选择的是左边的中位数,right天生能左移.
在这里插入图片描述

2. 总结

做题时按照上面的思路一步步去考虑,基本不会出错。步骤如下:

  1. 首先根据题目要求考虑,边界怎么选,是否需要改变搜索区间;
  2. 然后考虑搜索区间是否一定有目标值(索引),进而确定循环终止条件;
  3. 暂时写下mid = left + (right - left) / 2,根据题目要求判断mid的归属;
  4. 最后,写完循环体,根据left的滑动方法,判断mid是否需要更改。

这里就不举例了,题目太多了,建议大家去刷一刷LeetCode中的二分查找相关问题,将总结中的4个步骤记住,反复练习,很快就能上手大部分中等及以下的题目。

评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值