视频讲解:《算法不好玩》二分查找专题 (已经录制的部分,能够帮助大家完全搞懂二分查找算法,非常适合初学的朋友们)。
2022 年 12 月 27 日补充:有时间的话请大家先看上面的视频,2 倍速看完就好,知道前因后果以后,再做题就更有方向了。文字题解的内容还是更偏向于做题,直接给出了最优解,没有一步一步探索的过程。这里帮大家归纳一下重点:有一些问题可以在循环之中找到答案,所以写法是 while(left <= right) ,循环体内是三个分支。更多的问题需要等到退出循环以后得到答案,所以写法是 while(left < right) ,循环体内是两个分支。
实际上「二分查找」没有多少东西,一点儿也不难,觉得「二分查找」难写对的朋友,请认认真真做几个「二分查找」的问题,弄清楚每一行代码的意思,遇到死循环的时候仔细调试,弄清楚逻辑关系,而不要套别人给你写好的模版。
有很多问题 mid 有可能是问题的答案,所以不能排除掉,所以你看到的代码是 left = mid 与 right = mid,不要有思维定式,代码一定要写成 left = mid + 1 或者 right = mid - 1,而应该看题目怎么说。看清问题比记忆模版重要得多,不同的问题答案不一样,如果题目的意思都没弄清楚,怎么解决问题。
如果你在学习二分查找的过程中有很多问题,你可以先记下来,不用急着与解决它们,等你做了足够多的问题,有了足够的思考以后,你的问题会慢慢得到解答,我的学习过程也是这样,这个过程是很充实的。
阅读提示:内容比较多,核心思想帮大家归纳一下。如果没有时间看,建议把后面的问题做一下。
题解核心内容:所有模板都一样,不可以套模板,而应该 仔细看题(解题的关键在认真读题),分析清楚题目要找的答案需要满足什么性质。采用两边夹的方式,每一轮把待搜索区间分成两个部分,排除掉一定不是答案的区间,最后左右指针重合的地方就是我们要找的元素。
重要的事情说三遍:
- 如果你看我写的「二分查找」题解,请忘记掉「左闭右开」这件事情,它会对你有所干扰;
- 如果你看我写的「二分查找」题解,请忘记掉「左闭右开」这件事情,它会对你有所干扰;
- 如果你看我写的「二分查找」题解,请忘记掉「左闭右开」这件事情,它会对你有所干扰。
说明:我所有的关于「二分查找」法的题解,都会明确地标注「下一轮搜索区间是什么」,进而设置左边界 left 和 右边界 right。在我写的「二分查找」题解里,while(left < right) 不表示 循环不变量的区间定义是 [left..right),也就是说,如果我分析出,下一轮搜索区间是 [mid..10],我会设置 right = 10,而不会设置 right = 11。
while(left < right) 只表示它本来的意思:在 left < right 进入循环体。不要给它附加别的意思,这没有逻辑。
写在前面的话:
写对「二分查找」的重点,从来不在于「二分查找」我们用的是哪一个模板(所有的模板背后的逻辑都一样),更不在于我们设置的区间是「左闭右闭」还是「左开右闭」。而在于 认真看题、仔细分析题意,根据题目的条件和要求思考如何缩减区间,清楚地知道每一轮在什么样的情况下,搜索的范围是什么,进而设置左右边界。
我们需要分析清楚题目的意思,分析清楚要找的答案需要满足什么性质。应该清楚模板具体的用法,明白需要根据题意灵活处理、需要变通的地方,不可以认为每一行代码都是模板规定死的写法,不可以盲目套用、死记硬背。
二分查找只有一个思想,那就是:逐步缩小搜索区间。
本题解向大家介绍的,使用
left和right向中间靠拢的方法,有一个非常强的语义,那就是:当left与right重合的时候,我们就找到了问题的答案,使用这种写法有一个巨大的好处,那就是返回值不需要考虑返回left还是right,因为退出循环以后,它们是重合的。
重要的事情说三遍:
- 不是因为这种方法有用,而是因为很多问题就这么设计来着,一定要等到最后才能确定问题的解,在很多时候,不能在循环体中找到答案。
- 不是因为这种方法有用,而是因为很多问题就这么设计来着,一定要等到最后才能确定问题的解,在很多时候,不能在循环体中找到答案。
- 不是因为这种方法有用,而是因为很多问题就这么设计来着,一定要等到最后才能确定问题的解,在很多时候,不能在循环体中找到答案。
这一类问题解决的过程如下图所示:

在做题的过程中,会遇到两个难点:
- 取
mid的时候,有些时候需要+1,这是因为需要避免死循环; - 只把区间分成两个部分,这是因为:只有这样,退出循环的时候才有
left与right重合,我们才敢说,找到了问题的答案。
这两个难点,在练习的过程中,会逐渐清晰,不用着急一下子搞懂,事实上也不难理解。
本题解分为三个部分:
- 第一部分:本题题解。
如果只想看本题(「力扣」第 35 题)怎么解的,可以只看这里。
- 第二部分:二分查找常见问题回答。
重点概括了评论区常见的问题。
- 第三部分:二分查找题解列表(包含文字题解和视频题解)。
我花了很多时间做的教程,详细讲解了我是如何分析这些问题的,相信一定对你有帮助。
第一部分:本题题解
这一部分一定要分析清楚:
- 题目要找的元素是:第一个大于等于
target的元素的下标;- 数组的长度
len也有可能是问题的答案,「参考代码 2」设置right = len不是因为设置区间是「左闭右开」,而是因为len本来就有可能是问题的答案。上面 2 个小点,都需要仔细分析题意和几个示例得到,任何模板都不能回答这样的问题。
题意分析
根据示例,分析题目要我们返回的「插入元素的位置」是什么。
根据「示例 3」:
输入: [1, 3, 5, 6], 7
输出: 4
我们知道:如果目标元素 严格大于 输入数组中的最后一个元素,题目需要我们返回数组的最后一个元素的下标 +1(也就是数组的长度)。
又根据「示例 2」:
输入: [1, 3, 5, 6], 2
输出: 1
我们知道:题目要我们返回第 1 1 1 个 大于等于 目标元素 2 的下标(分析出这一点非常重要),因此返回 1。等于的情况可以看「示例 1」。
思路分析
在有序数组中查找,可以使用「二分查找」。
根据「题意分析」中对示例的描述:
- 情况 1:如果当前
mid看到的数值严格小于target,那么mid以及mid左边的所有元素就一定不是「插入元素的位置」,因此下一轮搜索区间是[mid + 1..right],下一轮把left移动到mid + 1位置,因此设置left = mid + 1; - 情况 2:否则,如果
mid看到的数值大于等于target,那么mid可能是「插入元素的位置」,mid的右边一定不存在「插入元素的位置」。如果mid的左边不存在「插入元素的位置」,我们才可以说mid是「插入元素的位置」。因此下一轮搜索区间是[left..mid],下一轮把right移动到mid位置,因此设置right = mid。
说明:上面的两点中,「情况 2」其实不用分析得那么细致, 因为只要「情况 1」的区间分析是正确的,「情况 2」一定是「情况 1」得到的区间的反面区间。
参考代码 1:
public class Solution {
public int searchInsert(int[] nums, int target) {
int len
深入理解二分查找:模板、区间设定与解题策略

本文详细介绍了二分查找算法的核心思想和常见误区,强调不应拘泥于固定的区间表示和模板,而应根据题目分析如何缩小搜索范围。作者指出,理解每轮循环的目的和边界设置是关键,同时提供了多种题解实例,以帮助读者更好地掌握二分查找的运用。
最低0.47元/天 解锁文章

被折叠的 条评论
为什么被折叠?



