博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
二分查找专题
阅读量:3528 次
发布时间:2019-05-20

本文共 2455 字,大约阅读时间需要 8 分钟。

二分查找法作为一种常见的查找方法,将原本是线性时间提升到了对数时间范围,大大缩短了搜索时间,具有很大的应用场景,而在LeetCode中,要运用二分搜索法来解的题目也有很多,但是实际上二分查找法的查找目标有很多种,而且在细节写法也有一些变化。根据查找的目标不同,二分查找法主要分为以下三类:

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

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

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

会返回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,不然就会出错。所以博主的建议是选择一套自己喜欢的写法,并且记住,实在不行就带简单的例子来一步一步执行,确定正确的写法也行。

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

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

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

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

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

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

这一类也比较常见,尤其是查找第一个大于目标值的数,在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
& nums, int target) {int left = 0, right = nums.size();while (left < right) {int mid = left + (right - left) / 2;if (nums[mid] <= target) left = mid + 1;else right = mid;} return right;}

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

转载地址:http://ywyhj.baihongyu.com/

你可能感兴趣的文章