二分查找法作为一种常见的查找方法,将原本是线性时间提升到了对数时间范围,大大缩短了搜索时间,具有很大的应用场景,而在 LeetCode 中,要运用二分搜索法来解的题目也有很多,但是实际上二分查找法的查找目标有很多种,而且在细节写法也有一些变化。之前有网友留言希望博主能针对二分查找法的具体写法做个总结,博主由于之前一直很忙,一直拖着没写,为了树立博主言出必行的正面形象,不能再无限制的拖下去了,那么今天就来做个了断吧,总结写起来~ (以下内容均为博主自己的总结,并不权威,权当参考,欢迎各位大神们留言讨论指正)

根据查找的目标不同,博主将二分查找法主要分为以下五类:

第一类: 需查找和目标值完全相等的数

这是最简单的一类,也是我们最开始学二分查找法需要解决的问题,比如我们有数组 [2, 4, 5, 6, 9],target = 6,那么我们可以写出二分查找法的代码如下:

int find(vector<int>& nums, int target) {
int left = , right = nums.size();
while (left < right) {
int mid = left + (right - left) / ;
if (nums[mid] == target) return mid;
else if (nums[mid] < target) left = mid + ;
else right = mid;
}
return -;
}

会返回3,也就是 target 的在数组中的位置。注意二分查找法的写法并不唯一,主要可以变动地方有四处:

第一处是 right 的初始化,可以写成 nums.size() 或者 nums.size() - 1。

第二处是 left 和 right 的关系,可以写成 left < right 或者 left <= right。

第三处是更新 right 的赋值,可以写成 right = mid 或者 right = mid - 1。

第四处是最后返回值,可以返回 left,right,或 right - 1。

但是这些不同的写法并不能随机的组合,像博主的那种写法,若 right 初始化为了 nums.size(),那么就必须用 left < right,而最后的 right 的赋值必须用 right = mid。但是如果我们 right 初始化为 nums.size() - 1,那么就必须用 left <= right,并且right的赋值要写成 right = mid - 1,不然就会出错。所以博主的建议是选择一套自己喜欢的写法,并且记住,实在不行就带简单的例子来一步一步执行,确定正确的写法也行。

第一类应用实例:

Intersection of Two Arrays

第二类: 查找第一个不小于目标值的数,可变形为查找最后一个小于目标值的数

这是比较常见的一类,因为我们要查找的目标值不一定会在数组中出现,也有可能是跟目标值相等的数在数组中并不唯一,而是有多个,那么这种情况下 nums[mid] == target 这条判断语句就没有必要存在。比如在数组 [2, 4, 5, 6, 9] 中查找数字3,就会返回数字4的位置;在数组 [0, 1, 1, 1, 1] 中查找数字1,就会返回第一个数字1的位置。我们可以使用如下代码:

int find(vector<int>& nums, int target) {
int left = , right = nums.size();
while (left < right) {
int mid = left + (right - left) / ;
if (nums[mid] < target) left = mid + ;
else right = mid;
}
return right;
}

最后我们需要返回的位置就是 right 指针指向的地方。在 C++ 的 STL 中有专门的查找第一个不小于目标值的数的函数 lower_bound,在博主的解法中也会时不时的用到这个函数。但是如果面试的时候人家不让使用内置函数,那么我们只能老老实实写上面这段二分查找的函数。

这一类可以轻松的变形为查找最后一个小于目标值的数,怎么变呢。我们已经找到了第一个不小于目标值的数,那么再往前退一位,返回 right - 1,就是最后一个小于目标值的数。

第二类应用实例:

 
第二类变形应用:Valid Triangle Number
 

第三类: 查找第一个大于目标值的数,可变形为查找最后一个不大于目标值的数

这一类也比较常见,尤其是查找第一个大于目标值的数,在 C++ 的 STL 也有专门的函数 upper_bound,这里跟上面的那种情况的写法上很相似,只需要添加一个等号,将之前的 nums[mid] < target 变成 nums[mid] <= target,就这一个小小的变化,其实直接就改变了搜索的方向,使得在数组中有很多跟目标值相同的数字存在的情况下,返回最后一个相同的数字的下一个位置。比如在数组 [2, 4, 5, 6, 9] 中查找数字3,还是返回数字4的位置,这跟上面那查找方式返回的结果相同,因为数字4在此数组中既是第一个不小于目标值3的数,也是第一个大于目标值3的数,所以 make sense;在数组 [0, 1, 1, 1, 1] 中查找数字1,就会返回坐标5,通过对比返回的坐标和数组的长度,我们就知道是否存在这样一个大于目标值的数。参见下面的代码:

int find(vector<int>& nums, int target) {
int left = , right = nums.size();
while (left < right) {
int mid = left + (right - left) / ;
if (nums[mid] <= target) left = mid + ;
else right = mid;
}
return right;
}

这一类可以轻松的变形为查找最后一个不大于目标值的数,怎么变呢。我们已经找到了第一个大于目标值的数,那么再往前退一位,返回 right - 1,就是最后一个不大于目标值的数。比如在数组 [0, 1, 1, 1, 1] 中查找数字1,就会返回最后一个数字1的位置4,这在有些情况下是需要这么做的。

第三类应用实例:

Kth Smallest Element in a Sorted Matrix

第三类变形应用示例:

Sqrt(x)

第四类: 用子函数当作判断关系(通常由 mid 计算得出)

这是最令博主头疼的一类,而且通常情况下都很难。因为这里在二分查找法重要的比较大小的地方使用到了子函数,并不是之前三类中简单的数字大小的比较,比如 Split Array Largest Sum 那道题中的解法一,就是根据是否能分割数组来确定下一步搜索的范围。类似的还有 Guess Number Higher or Lower 这道题,是根据给定函数 guess 的返回值情况来确定搜索的范围。对于这类题目,博主也很无奈,遇到了只能自求多福了。

第四类应用实例:

Split Array Largest Sum, Guess Number Higher or Lower,Find K Closest Elements,Find K-th Smallest Pair Distance,Kth Smallest Number in Multiplication Table,Maximum Average Subarray II,Minimize Max Distance to Gas Station,Swim in Rising Water,Koko Eating Bananas,Nth Magical Number

第五类: 其他(通常 target 值不固定)

有些题目不属于上述的四类,但是还是需要用到二分搜索法,比如这道 Find Peak Element,求的是数组的局部峰值。由于是求的峰值,需要跟相邻的数字比较,那么 target 就不是一个固定的值,而且这道题的一定要注意的是 right 的初始化,一定要是 nums.size() - 1,这是由于算出了 mid 后,nums[mid] 要和 nums[mid+1] 比较,如果 right 初始化为 nums.size() 的话,mid+1 可能会越界,从而不能找到正确的值,同时 while 循环的终止条件必须是 left < right,不能有等号。

类似的还有一道 H-Index II,这道题的 target 也不是一个固定值,而是 len-mid,这就很意思了,跟上面的 nums[mid+1] 有异曲同工之妙,target 值都随着 mid 值的变化而变化,这里的right的初始化,一定要是 nums.size() - 1,而 while 循环的终止条件必须是 left <= right,这里又必须要有等号,是不是很头大 -.-!!!

其实仔细分析的话,可以发现其实这跟第四类还是比较相似,相似点是都很难 -.-!!!,第四类中虽然是用子函数来判断关系,但大部分时候 mid 也会作为一个参数带入子函数进行计算,这样实际上最终算出的值还是受 mid 的影响,但是 right 却可以初始化为数组长度,循环条件也可以不带等号,大家可以对比区别一下~

第五类应用实例:

Find Peak Element

H-Index II

综上所述,博主大致将二分搜索法的应用场景分成了主要这五类,其中第二类和第三类还有各自的扩展。根据目前博主的经验来看,第二类和第三类的应用场景最多,也是最重要的两类。第一类,第四类,和第五类较少,其中第一类最简单,第四类和第五类最难,遇到这类,博主也没啥好建议,多多练习吧~

如果有写的有遗漏或者错误的地方,请大家踊跃留言啊,共同进步哈~

LeetCode All in One 题目讲解汇总(持续更新中...)

LeetCode Binary Search Summary 二分搜索法小结的更多相关文章

  1. [LeetCode] Binary Search 二分搜索法

    Given a sorted (in ascending order) integer array nums of n elements and a target value, write a fun ...

  2. LeetCode Monotone Stack Summary 单调栈小结

    话说博主在写Max Chunks To Make Sorted II这篇帖子的解法四时,写到使用单调栈Monotone Stack的解法时,突然脑中触电一般,想起了之前曾经在此贴LeetCode Al ...

  3. LeetCode Binary Search All In One

    LeetCode Binary Search All In One Binary Search 二分查找算法 https://leetcode-cn.com/problems/binary-searc ...

  4. LeetCode & Binary Search 解题模版

    LeetCode & Binary Search 解题模版 In computer science, binary search, also known as half-interval se ...

  5. 153. Find Minimum in Rotated Sorted Array(leetcode, binary search)

    https://leetcode.com/problems/find-minimum-in-rotated-sorted-array/description/ leetcode 的题目,binary ...

  6. [LeetCode] Binary Search Tree Iterator 二叉搜索树迭代器

    Implement an iterator over a binary search tree (BST). Your iterator will be initialized with the ro ...

  7. LeetCode Binary Search Tree Iterator

    原题链接在这里:https://leetcode.com/problems/binary-search-tree-iterator/ Implement an iterator over a bina ...

  8. [Leetcode] Binary search -- 475. Heaters

    Winter is coming! Your first job during the contest is to design a standard heater with fixed warm r ...

  9. [Leetcode] Binary search, Divide and conquer--240. Search a 2D Matrix II

    Write an efficient algorithm that searches for a value in an m x n matrix. This matrix has the follo ...

随机推荐

  1. Linux下的指令:find,which

    1. 在Linux系统下,使用find来查找文件: find [path] [condition] [operation] path指定了在哪个目录查找,condition限定了查找条件,operat ...

  2. Solr+Tomcat+zookeeper部署实战

    一 .安装solr 环境说明:centos 7.3,solr 6.6,zookeeper3.4,Tomcat8.5,jdk1.8 zookeeper的部署请参考:http://www.cnblogs. ...

  3. oracle导入dmp文件的2种方法

    使用imp.impdp方式导入数据 1.使用imp导入数据 打开cmd窗口,然后直接敲入一下命令即可,需要注意的是,要事先把dmp文件放到正确的路径中去 imp yx_base/@yx_192. fi ...

  4. I Know Alpha冲刺随笔集

    Alpha冲刺 Day1 Alpha冲刺 Day2 Alpha冲刺 Day3 Alpha冲刺 Day4 Alpha冲刺 Day5 Alpha冲刺 Day6 Alpha冲刺 Day7 Alpha冲刺 D ...

  5. alpha冲刺第七天

    一.合照 二.项目燃尽图 三.项目进展 问答界面问答内容呈现 设置里的帐号设置呈现 能爬取教务处网站的内容保存到本地数据库 四.明日规划 继续完善各个内容的界面呈现 查找关于如何自动更新爬取内容 搜索 ...

  6. C语言第二周作业——分支结构

    一.PTA实验作业 题目1.7-1计算分段函数 本题目要求计算下列分段函数f(x)的值: 1实验代码 double x,result; scanf("%lf",&x); i ...

  7. C语言博客作业一二维数组

    一.PTA实验作业 题目1.7-5 数组循环左移 1.本题PTA提交列表 2.设计思路 定义变量a[100]存放整数,整数n,整数m,change作为交换数组的媒介,j,i作为循环的变量 输入整数n, ...

  8. 201621123040《Java程序设计》第六周学习总结

    1.本周学习总结 1.1 面向对象学习暂告一段落,请使用思维导图,以封装.继承.多态为核心概念画一张思维导图或相关笔记,对面向对象思想进行一个总结 2.书面作业 2.1clone方法 2.1.1在te ...

  9. XFTP连接主机文件名显示中文乱码且不能下载的解决方法

    Xftp连接主机文件名显示中文乱码且不能下载的本地解决方法 原因:Xftp编码格式问题 解决方法:把Xftp的编码格式增加UTF-8 具体步骤:打开Xftp,文件-属性,在打开的属性界面中打开&quo ...

  10. nyoj 第几是谁

    第几是谁? 时间限制:3000 ms  |  内存限制:65535 KB 难度:3   描述 现在有"abcdefghijkl"12个字符,将其按字典序排列,如果给出任意一种排列, ...