力扣-二分法想法
二分法是折半算法,每次二分的时候,都能排除当前一半的数据,因此该算法的时间复杂度为O(log n) 是一种十分优的查找算法,也正是因为该极优的时间复杂度,也导致(或者是源于)对场景要求较为严格,必须是排序数组。当然,有时候我们也会在一些特殊场景中使用二分法,但本质还是基于排序数组的。
我刷了不少关于二分法的题目,每次刷都会有不同的体会,我总结如下所示。
1.不变量的思想:在二分法中,我们会有两个指针,一个左指针left 和一个右指针right 我们对这两个指针赋予的含义为:我们的搜索区间为[left,right],left是搜索区间的左边界,right是搜索区间的右边界,这层含义是不变的,变的是什么?是left和right具体的值,这也是必须变的,如果不变,区间也不会缩小。
2.排除法的思想:比如在查找一个元素,此时搜索区间为[left, right] 我们不能保证元素一定在[left,right]区间之内,因为这个数组可能压根就没有这个元素,但我们可以确定是[0,left - 1]区间肯定没有目标元素,[right + 1, nums.length - 1]之间肯定也没有这个元素,而且,还有一点可以确认的是,[0, left - 1]之间的元素肯定是小于目标元素的,而[right + 1, nums.length - 1]之间的元素肯定是大于目标元素的,因为在代码中,我们就是这样来移动left和right指针的。
我不知道大家对排除法思想有了解多少,以力扣的一道题目为例:35. 搜索插入位置
首先,如果不用二分查找法,我们直接暴力遍历就行,从左向右找,找到第一个大于目标元素的位置(针对不存在目标元素的情况)。
在排除法中,我们说过[right + 1, nums.length - 1]是永远大于目标元素的;而[0, left - 1]是永远小于目标元素的,我也不知道这算不算是不变量的一种体现。当我们在数组中找不到目标元素的时候,最后满足left = right + 1,所以从left这个位置开始,往后,都是大于目标元素的,而在left之前所有元素都是小于目标元素的,这时候,我们找到了第一个大于目标元素的位置,就是left
补充:
1. 关于开闭区间的问题:[left, right]和[left,right) 其实,我觉得没必要纠结。因为这两种是可以转换的,如果我们掌握了[left,right]的写法,那么对于[left,right) 其实是可以转换为闭区间的:[left, right - 1] 在[left, right]闭区间的基础上,把right改为right - 1即可。