LeetCode Binary Search Summary 二分搜索法小结

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

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

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

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

int find(vector<int>& 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

但是这些不同的写法并不能随机的组合,像博主的那种写法,若right初始化为了nums.size(),那么就必须用left < right,而最后的right的赋值,用哪个都可以,博主偷懒就不写-1了。但是如果我们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 = 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,就是第一个小于目标值的数。

第二类应用实例:

HeatersArranging CoinsValid Perfect SquareMax Sum of Rectangle No Larger Than KRussian Doll Envelopes

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

这一类也比较常见,尤其是查找第一个大于目标值的数,在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 = 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,这在有些情况下是需要这么做的。

第三类应用实例:

Kth Smallest Element in a Sorted Matrix

第四类: 用子函数当作判断关系

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

第四类应用实例:

Split Array Largest SumGuess Number Higher or Lower

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

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

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

时间: 2024-10-15 03:04:30

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

[LeetCode]Binary Search Tree Iterator,解题报告

题目 LeetCode题目如下: mplement an iterator over a binary search tree (BST). Your iterator will be initialized with the root node of a BST. Calling next() will return the next smallest number in the BST. Note: next() and hasNext() should run in average O(1

[LeetCode] Binary Search Tree Iterator

Binary Search Tree Iterator Implement an iterator over a binary search tree (BST). Your iterator will be initialized with the root node of a BST. Calling next() will return the next smallest number in the BST. Note: next() and hasNext() should run in

[Leetcode] Binary search/tree-230. Kth Smallest Element in a BST

Given a binary search tree, write a function kthSmallest to find the kth smallest element in it. Note: You may assume k is always valid, 1 ? k ? BST's total elements. Follow up:What if the BST is modified (insert/delete operations) often and you need

LeetCode——Binary Search Tree Iterator

Description: Implement an iterator over a binary search tree (BST). Your iterator will be initialized with the root node of a BST. Calling next() will return the next smallest number in the BST. Note: next() and hasNext() should run in average O(1) t

[leetcode] Binary Search Tree

题目:(Tree Stack) Implement an iterator over a binary search tree (BST). Your iterator will be initialized with the root node of a BST. Calling next() will return the next smallest number in the BST. Note: next() and hasNext() should run in average O(1

[Leetcode] Binary search -- 222. Count Complete Tree Nodes

Given a complete binary tree, count the number of nodes. Definition of a complete binary tree from Wikipedia:In a complete binary tree every level, except possibly the last, is completely filled, and all nodes in the last level are as far left as pos

[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 following properties: Integers in each row are sorted in ascending from left to right. Integers in each column are sorted in ascending from top to bottom.

Leetcode Binary Search Conclusion

278 First Bad Version23.7%Easy 278. First Bad Version 349 Intersection of Two Arrays44.5%Easy 374 Guess Number Higher or Lower32.2%Easy 350 Intersection of Two Arrays II42.6%Easy 270 Closest Binary Search Tree Value 36.3%Easy 50 Pow(x, n)27.4%Medium

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

Implement an iterator over a binary search tree (BST). Your iterator will be initialized with the root node of a BST. Calling next() will return the next smallest number in the BST. Note: next() and hasNext() should run in average O(1) time and uses