二分搜索详解
一、形式:
- 第一种(左闭右闭形式)[l,r],这种每次搜索区间为 [m+1,r] 或 [l,m-1]
重点:当 l = r 时,区间为 [l, l],此时只有一个数,但仍要判断,因为二分搜索可能不存在这个值
l = 0,r = v.size()-1;
while(l <= r){ //终止条件 l = r + 1
int m = (l + r)/2
if(target == v[m]){
...
}else if(v[m] > target){
r = m-1;
}else{
l = m+1;
}
}
- 第二种(左闭右开形式)[l,r), 这种每次搜索区间 [l,m) 或 [m+1,r)
重点,当 l = r 时,[l, l) 区间为空,即结束空间条件 l == r
l = 0,r = v.size();
while(l < r){ //终止条件 l = r
int m = (l+r)/2;
if(target == v[m]){
...
}else if(v[m] > target){
r = m;
}else{
l = m+1;
}
}
左右侧边界的值
[1, 2 , 2 , 2, 3 ,5]
- 左侧边界即 1
l = 0, r = v.size();
while(l < r){
int m = (l + r )/2;
if(v[m] < target) // [ )形式最终 l == r,由于当 v[m] == target时 r = m,
// 则最后 l == r时 ,实际为l处的值,即第一个2位置1
l = m + 1;
else
r = m;
}
return l;
- 右侧边界
l = 0,r = v.size();
while(l < r){
int m = (l + r)/2;
if(v[m] <= target){// [,)形式,最终 l == r,
//当 v[m] == m时,l = m+1,最后l == r时,实际为 r 处,即3的 位置 4
l = m + 1;
}else{
r = m;
}
}
return l-1;
二分搜索灵活运用
当二分查找有目标值时 f[m] 与 target 比较,当没有目标指时,通常与端点 l 或 r 比较。
为了:当进行一次比较时,一定能够确定答案在mid的某一侧。一次比较为 arr[mid]跟谁比的问题
有一个长度为 n 的非降序数组,比如[1,2,3,4,5],将它进行旋转,即把一个数组最开始的若干个元素搬到数组的末尾,变成一个旋转数组,比如变成了[3,4,5,1,2],或者[4,5,1,2,3]这样的。请问,给定这样一个旋转数组,求数组中的最小值。
数据范围:1 \le n \le 100001≤n≤10000,数组中任意元素的值: 0 \le val \le 100000≤val≤10000
要求:空间复杂度:O(1)O(1) ,时间复杂度:O(logn)O(logn)
示例1
输入:
[3,4,5,1,2]
返回值:
1
示例2
输入:
[3,100,200,3]
返回值:
3
思路:数组是非降序数组,此题 比较右端点,旋转后 [l,k] 非降序 ,[k,r] 非降序
- 当 f[m] < f[r] 时,说明 m 到 r 非降序,最小值在左边,r = m;
- 当 f[m] == f[r] 时, r = r -1 ;缩小空间
- 当 f[m] > f[r] 时,说明旋转了,最小值在 [l,m]内, l = m + 1;
假设比较左端点:
- 当 f[m] < f[l] 时,说明 [m,r] 是非降序,最小值在 [l,m] 中,r = m;
- 当 f[m] > f[l] 时, 说明 [l,m] 非降序 (可能没有旋转,可能旋转),结果 可能 在左边 可能在右边
class Solution {
public:
int minNumberInRotateArray(vector<int> rotateArray) {
int l = 0,r = rotateArray.size()-1;
while(l < r){
int m = l + (r - l)/2;
if(rotateArray[m] > rotateArray[r]){
l = m + 1;
}else if(rotateArray[m] == rotateArray[r]){
r = r-1;
}else{
r = m;
}
}
return rotateArray[l];
}
};