二分查找重难点总结(边界条件、循环条件、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个步骤记住,反复练习,很快就能上手大部分中等及以下的题目。

  • 36
    点赞
  • 69
    收藏
    觉得还不错? 一键收藏
  • 5
    评论
二分查找是一种快速查找的方法,它可以在有序数组中查找指定元素的位置。首先,我们需要对数组进行初始化,即确定左右指针的初始位置。然后,利用二分法的思想,将待查找的元素与数组的中间元素进行比较。如果中间元素小于等于待查找元素,则将左指针移动到中间元素的位置。如果中间元素大于待查找元素,则将右指针移动到中间元素的位置。重复以上步骤,直到找到目标元素或者左指针等于右指针。 在Python中实现二分查找可以按照以下步骤进行: 1. 初始化左右指针为数组的边界值,即左指针为-1,右指针为数组长度。 2. 当左指针加1不等于右指针时,执行以下循环。 3. 计算中间位置m,通过将左指针与右指针之和除以2得到。 4. 若中间元素小于等于待查找元素,则将左指针移动到中间位置。 5. 若中间元素大于待查找元素,则将右指针移动到中间位置。 6. 返回右指针所在位置的元素,即为目标元素。 这是用Python实现二分查找的示例代码: ```python def binary_search(arr, key): N = len(arr) l, r = -1, N # 初始化左右指针的位置 while l + 1 != r: # 当左指针加1不等于右指针时执行循环 m = int(l + (r - l) / 2) # 计算中间位置m if arr[m <= key: # 若中间元素小于等于待查找元素 l = m # 将左指针移动到中间位置 else: r = m # 若中间元素大于待查找元素,则将右指针移动到中间位置 return arr[r # 返回右指针所在位置的元素 if __name__ == '__main__': arr = [1, 2, 3, 4, 5, 5, 5, 8, 9] key = 5 num = binary_search(arr, key) print(num) ``` 以上是关于二分查找的边界问题的Python实现方法。通过对左右指针的初始化和移动,我们可以在有序数组中快速查找指定元素的位置。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *3* [二分查找 边界查找](https://blog.csdn.net/CCSUXWZ/article/details/120771067)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] - *2* [Python有序查找算法二分法实例分析](https://download.csdn.net/download/weixin_38627769/13774169)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值