0704. 二分查找 (记住!经典模型)
class Solution {
public:
int search(vector<int>& nums, int target) {
int L=0;
int R=nums.size()-1;
while(L<R){
int mid=L+(R-L)/2;
if(nums[mid]==target) return mid;
else if(nums[mid]>target) R=mid-1;
else L=mid+1;
}
return -1;
}
};
两次二分查找完美解决
class Solution {
public:
vector<int> searchRange(vector<int>& nums, int target) {
if (nums.size() == 0)
return {-1,-1};
// 两次二分查找,分开查找第一个和最后一个
// 时间复杂度 O(log n), 空间复杂度 O(1)
// [1,2,3,3,3,3,4,5,9]
int left = 0;
int right = nums.size() - 1;
int first = -1;
int last = -1;
// 找第一个等于target的位置
while (left <= right) {
int middle = (left + right) / 2;
if (nums[middle] == target) {
first = middle;
right = middle - 1; //重点,每次都向前,刷新
} else if (nums[middle] > target) {
right = middle - 1;
} else {
left = middle + 1;
}
}
// 最后一个等于target的位置
left = 0;
right = nums.size() - 1;
while (left <= right) {
int middle = (left + right) / 2;
if (nums[middle] == target) {
last = middle;
left = middle + 1; //重点,每次都向后,刷新
} else if (nums[middle] > target) {
right = middle - 1;
} else {
left = middle + 1;
}
}
return vector<int>{first, last};
}
};
如果没有复杂度的限制:一个赖皮方法,sort
还有一个方法:抓住下降这一步。
class Solution {
public:
int findMin(vector<int>& nums) {
int div = 0;
int n = 0;
for ( int num : nums) {
div = num - n;
n = num;
if (div < 0)
return num;
}
return nums[0];
}
};
你必须设计一个时间复杂度为 O(log n)
的算法解决此问题。那么就要使用二分查找了
class Solution {
public:
int findMin(vector<int>& nums) {
int L=0;
int R=nums.size()-1;
int mid;
while(L<R){
mid=L+(R-L)/2;
if(nums[mid]<nums[R]){
R=mid;
}
else L=mid+1;
}
return nums[L];
}
};
关于其中边界的判断
// 二分查找
while(low < high){
// 取中间值
int mid = (high+low)/2;
// 如果中间值小于最大值,则最大值减小
// 疑问:为什么 high = mid;而不是 high = mid-1;
// 解答:{4,5,1,2,3},如果high=mid-1,则丢失了最小值1
if (nums[mid] < nums[high]) {
high = mid;
} else {
// 如果中间值大于最大值,则最小值变大
// 疑问:为什么 low = mid+1;而不是 low = mid;
// 解答:{4,5,6,1,2,3},nums[mid]=6,low=mid+1,刚好nums[low]=1
// 继续疑问:上边的解释太牵强了,难道没有可能low=mid+1,正好错过了最小值
// 继续解答:不会错过!!! 如果nums[mid]是最小值的话,则其一定小于nums[high],走if,就不会走else了
low = mid+1;
}
}
// 疑问:为什么while的条件是low<high,而不是low<=high呢
// 解答:low<high,假如最后循环到{*,10,1,*}的这种情况时,nums[low]=10,nums[high]=1,nums[mid]=10,low=mid+1,
// 直接可以跳出循环了,所以low<high,此时low指向的就是最小值的下标;
// 如果low<=high的话,low=high,还会再不必要的循环一次,此时最后一次循环的时候会发生low==high==mid,
// 则nums[mid]==nums[high],则会走一次else语句,则low=mid+1,此时low指向的是最小值的下一个下标,
// 则需要return[low-1]
return nums[low];
}