二分查找是算法中常用的思想,但是怎么二分又内有乾坤
二分核心思想
「二分」的本质是两段性,并非单调性。只要一段满足某个性质,另外一段不满足某个性质,就可以用「二分」。
二分解法模板
两个模板,不是用哪个都行,需要分析题意。
- 如果是最简单的二分,查找值,那么用哪个无所谓。(一般都不会)
- 如果是两次二分,或者是需要找出边界值。就得看你需要左边还是右边来动态选择模板了
模板1
当我们将区间[l,r]划分成[l,mid]和[mid+1,r]时,其更新操作是r=mid或者l=mid+1,计算mid时不需要加1,即mid=(l+r)/2
long l = 0, r = 1000009;
while (l < r) {
long mid = l + r >> 1;
if (check(mid)) { //条件
r = mid;
} else {
l = mid + 1;
}
}
int bsearch_1(int x){
int l = 0,r = n - 1;
while(l < r){
int mid = l + r >> 1;
if(q[mid] >= x) r = mid;
else l = mid + 1;
}
return l;
}
用这个模板的就表示最后的left和right都是偏左方的
模板2
当我们将区间[l,r]划分成[l,mid−1]和[mid,r]时,其更新操作是r=mid−1或者l=mid,此时为了防止死循环,计算mid时需要加1,即mid=(l+r+1)/2。 偏右方
int l = 0, r = n - 1;
while (l < r) {
int mid = l + r + 1 >> 1;
if (nums[mid] <= nums[0]) { //这里是你的条件
l = mid;
} else {
r = mid - 1;
}
}
int bsearch_2(int x){
int l = 0, r = n - 1;
while(l < r){
int mid = l + r + 1 >> 1;
if(q[mid] <= x) l = mid;
else r = mid - 1;
}
return l;
}
总结
简单的总结:当需要找分界点的时候,使用l=mid-1的,主要就是防止分界点是最后一个
这题便就是只能使用缩短左边界
已知一个长度为 n 的数组,预先按照升序排列,经由 1 到 n 次 旋转 后,得到输入数组。例如,原数组 nums = [0,1,2,4,5,6,7] 在变化后可能得到:
- 若旋转
4次,则可以得到[4,5,6,7,0,1,2]
- 若旋转
7次,则可以得到[0,1,2,4,5,6,7]
注意,数组 [a[0], a[1], a[2], ..., a[n-1]]旋转一次 的结果为数组 [a[n-1], a[0], a[1], a[2], ..., a[n-2]] 。
给你一个元素值 互不相同 的数组 nums ,它原来是一个升序排列的数组,并按上述情形进行了多次旋转。请你找出并返回数组中的 最小元素 。
你必须设计一个时间复杂度为 O(log n) 的算法解决此问题。
示例 1:
输入:nums = [3,4,5,1,2]
输出:1
解释:原数组为 [1,2,3,4,5] ,旋转 3 次得到输入数组。
示例 2:
输入:nums = [4,5,6,7,0,1,2]
输出:0
解释:原数组为 [0,1,2,4,5,6,7] ,旋转 3 次得到输入数组。
示例 3:
输入:nums = [11,13,15,17]
输出:11
解释:原数组为 [11,13,15,17] ,旋转 4 次得到输入数组。