目录
二分查找专题
-
基本框架
int binarySearch(vector<int>& nums,int target)
{
int left = 0;
int right = ...;
int mid = 0;
while(...){
mid = left + ((right-mid) >> 1);
if(nums[mid] == target)
...;
else if(nums[mid] < target)
left = ...;
else if(nums[mid] > target)
right = ...;
}
return ...;
}
-
常见的几个问题
1.right的值,是nums.size() - 1 还是 nums.size() ?
如果right = nums.size(),则搜索区间为[ left , right ),是左闭右开区间
如果right = nums.size() - 1,则搜索区间为[ left , right ],是左右都闭合区间
2.while循环条件,是 left <= right 还是 left < right ?
如果是<,则说明 left 不可以等于 right,搜索的是左闭右开区间[ left , right ),即对应的 right 初始化值应该为 right = nums.size(); 终止条件为 left == right;
如果是<=,则说明 left 可以等于 right,搜索的是左右都闭合区间[ left , right ],即对应的 right 初始化值应该为 right = nums.size() - 1; 终止条件为 left = right + 1;
3.返回值
如果前面right = nums.size() ,那么while循环条件应该为 left < right,则返回值需要检查,因为当nums = {5}只有一个元素时,left == right 直接跳出,返回值left或right都一样,要处理left
如果前面right = nums.size() - 1,那么while循环条件应该为 left <= right,则返回值需要检查left或者right是否越界,因为left == right时也会处理
4.mid值的计算
mid = ( left + right ) / 2,两个int型相加可能出现溢出
mid = left + (right - left) / 2,变形防止溢出,但除法操作比较慢
mid = left + ((right - left) >> 1),位运算操作速度更快,但是要注意:"+"的优先级高于位运算符">>",需要加括号
-
实战
难度递增,代码统一风格为左右都闭合区间
class Solution {
public:
int search(vector<int>& nums, int target) {
int size = nums.size();
if(size <= 0) return -1;
int left = 0;
int right = size-1;
int mid = 0;
while(left <= right){
mid = left + (( right - left ) >> 1);
if(nums[mid] == target)
return mid;
else if(nums[mid] > target)
right = mid - 1;
else
left = mid + 1;
}
return -1;
}
};
class Solution {
public:
int searchInsert(vector<int>& nums, int target) {
int size = nums.size();
if(size <= 0) return -1;
int left = 0;
int right = size-1;
int mid = 0;
while(left <= right){
mid = left +((right-left)>>1);
if(nums[mid] == target)
return mid;
else if(nums[mid] > target)
right = mid - 1;
else if(nums[mid] < target)
left = mid + 1;
}
return left;
}
};
class Solution {
public:
vector<int> searchRange(vector<int>& nums, int target) {
int size = nums.size();
if(size <= 0) return {-1,-1};
vector<int> res;
res.push_back(searchFirst(nums,target,size));
res.push_back(searchLast(nums,target,size));
return res;
}
int searchFirst(vector<int>& nums, int target,int size){
int left = 0;
int right = size -1;
int mid = 0;
while(left <= right){
mid = left + ((right-left)>>1);
if(nums[mid] == target)
right = mid - 1;
else if(nums[mid] > target)
right = mid - 1;
else if(nums[mid] < target)
left = mid + 1;
}
//最后left可能越界
if(left >= size || nums[left] != target)
return -1;
return left;
}
int searchLast(vector<int>& nums, int target,int size){
int left = 0;
int right = size -1;
int mid = 0;
while(left <= right){
mid = left + ((right-left)>>1);
if(nums[mid] == target)
left = mid + 1;
else if(nums[mid] > target)
right = mid - 1;
else if(nums[mid] < target)
left = mid + 1;
}
//最后right可能越界
if(right < 0 || nums[right] != target)
return -1;
return right;
}
};
class Solution {
public:
int findMin(vector<int>& nums) {
int size = nums.size();
if(size <= 0) return -1;
int left = 0;
int right = size -1;
int mid = 0;
while(left <= right){
mid = left + ((right-left) >> 1);
//将数组分为左右两部分,要找的最小值就是把数组分为两部分的数
//看最后一个数,因为最后一个数是右半部分最大值
//如果mid比右半部分最大值大,说明最小的数在右半部分
//如果mid比右半部分最大值小,说明最小的数在左半部分
//如果mid等于右半部分最大值,说明现在left == right,则必须跳出,否则死循环
if(nums[mid] > nums[right])
left = mid + 1;
else if(nums[mid] < nums[right])
right = mid;
else if(nums[mid] == nums[right])
break;
}
return nums[left];
}
};
class Solution {
public:
int findMin(vector<int>& nums) {
int size = nums.size();
if(size <= 0) return -1;
int left = 0;
int right = size -1;
int mid = 0;
while(left <= right){
mid = left+((right-left)>>1);
if(nums[mid] == nums[right])
//不能确定向左或向右,线性缩小区间
right--;
else if(nums[mid] > nums[right])
left = mid + 1;
else if(nums[mid] < nums[right])
right = mid;
}
return nums[left];
}
};
class Solution {
public:
int search(vector<int>& nums, int target) {
int size = nums.size();
if(size <= 0) return -1;
int left = 0;
int right = size - 1;
int mid = 0;
while(left <= right){
mid = left + ((right - left) >> 1);
if(nums[mid] == target)
return mid;
//[left,mid]是连续递增的
if(nums[left] <= nums[mid]){
//target在[left,mid)之间,到[left,mid-1]找
if(nums[left] <= target && target < nums[mid])
right = mid - 1;
//target在(mid,right]之间,到[mid+1,right]找
else
left = mid + 1;
}
//[mid,right]是连续递增的
else if(nums[left] > nums[mid]){
//target在(mid,right]之间,到[mid+1,right]找
if(nums[mid] < target && target <= nums[right])
left = mid + 1;
//target在[left,mid)之间,到[left,mid-1]找
else
right = mid - 1;
}
}
return -1;
}
};
class Solution {
public:
bool search(vector<int>& nums, int target) {
//含重复值
int size = nums.size();
if(size <= 0) return false;
int left = 0;
int right = size - 1;
int mid = 0;
while(left <= right){
mid = left + ((right - left) >> 1);
if(nums[mid] == target)
return true;
if(nums[left] == nums[mid]){
//nums[left] == nums[mid]时,无法判断向左还是向右,缩小区间
left++;
continue;
}
//[left,mid]连续递增
if(nums[left] <= nums[mid]){
//[left,mid)中找target
if(nums[left] <= target && target < nums[mid])
right = mid - 1;
//(mid,right]中找target
else
left = mid + 1;
}
//[mid,right]连续递增
else{
//(mid,right]中找target
if(nums[mid] < target && target <= nums[right])
left = mid + 1;
//[left,mid)中找target
else
right = mid - 1;
}
}
return false;
}
};
图解待后续补充。