目录
例1
注意:
①left <= right,这里的=号是最后一次通过下标mid来判断
②在偶数的时候mid,左右无所谓,因为left和right都有1;
参考代码
cpp
class Solution {
public:
int search(vector<int>& nums, int target) {
int left = 0, right = nums.size() - 1;
while(left <= right)
{
int mid = left + (right - left) / 2;
if(nums[mid] < target) left = mid + 1;
else if(nums[mid] > target) right = mid - 1;
else return mid;
}
return -1;
}
};
例2
我把int mid = left + (right - left) / 2;称为左偏
int mid = left + (right - left + 1) / 2;称为右偏
死循环情况如下
如果左偏时,left=mid,那么就不会动了
概括为:左偏left要 + 1,右偏right 要 -1(+-1是相较与mid)
分析:有二段性才可以用二分查找,也就是找二段性
左边段:<target 右边段:>=target
注意这里是left<right,通过出循环时这个位置与target比较,来判断有没有这个值
参考代码
cpp
class Solution {
public:
vector<int> searchRange(vector<int>& nums, int target) {
if(nums.size() == 0) return {-1, -1};
int left = 0, right = nums.size() - 1;
while(left < right)
{
int mid = left + (right - left) / 2;
if(nums[mid] < target) left = mid + 1;
else right = mid;
}
if(nums[left] != target) return {-1, -1};
int begin = left;
right = nums.size() - 1;
while(left < right)
{
int mid = left + (right - left + 1) / 2;
if(nums[mid] > target) right = mid - 1;
else left = mid;
}
return {begin, right};
}
};
例3
这题就是个左偏,
临界点是:>=target,最终left和right是要停在这个位置
为什么会想到最后的if条件? 解释:这是个数组,要返回下标,又往里面插入元素,那么原来的下标范围是[0, nums.size() - 1],那现在个数+1了,最后一个位置返回不了
参考代码
cpp
class Solution {
public:
int searchInsert(vector<int>& nums, int target) {
int left = 0, right = nums.size() - 1;
while(left < right)
{
int mid = left + (right - left) / 2;
if(nums[mid] < target) left = mid + 1;
else right = mid;
}
if(nums[left] < target) return nums.size();
return left;
}
};
例4
解析:临界是<= 那就是右偏,与上面有点不同的是,不是直接拿值比较,多了一步(mid * mid),也没多大差别,条件有点不同,右偏就是left不动,left就是有效值,right之所以-1,因为right所在的位置一定不是,然后再是判断mid是否+1
注意:这边的溢出mid是int就够的,但是如果right = INT_MAX,left=0,那么括号里面的式子就会溢出,下面的mid*mid也会溢出,还有这里的整形提升是有顺序的,int mid = left + (long long)(right - left + 1) / 2;这么写就不对,这是结果强转,要先强转里面的
参考代码
cpp
class Solution {
public:
int mySqrt(int x) {
int left = 0, right = x;
while(left < right)
{
int mid = left + ((long long)right - left + 1) / 2;
if((long long)mid * mid <= x) left = mid;
else right = mid - 1;
}
return left;
}
};
例5
852. 山脉数组的峰顶索引162. 寻找峰值这两题代码一样
这题左右偏都可以,找到二段性
这里mid就是有效位置,左偏一定是下标mid和mid-1,这样才能使得left一直在上坡上段,右偏一定是mid和mid+1,使right一直在下坡段
右偏参考代码
cpp
class Solution {
public:
int peakIndexInMountainArray(vector<int>& arr) {
int left = 0, right = arr.size() - 1;
while(left < right)
{
int mid = left + (right - left + 1) / 2;
if(arr[mid] > arr[mid - 1]) left = mid;
else right = mid - 1;
}
return left;
}
};
左偏参考代码
cpp
class Solution {
public:
int peakIndexInMountainArray(vector<int>& arr) {
int left = 0, right = arr.size() - 1;
while(left < right)
{
int mid = left + (right - left) / 2;
if(arr[mid] > arr[mid + 1]) right = mid;
else left = mid + 1;
}
return left;
}
};
例6
与nums[nums.size() - 1]比较的参考代码
cpp
class Solution {
public:
int findMin(vector<int>& nums) {
int left = 0, right = nums.size() - 1;
while(left < right)
{
int mid = left + (right - left) / 2;
if(nums[mid] > nums[nums.size() - 1]) left = mid + 1;
else right = mid;
}
return nums[left];
}
};
与nums[0]比较的参考代码,多了的if就是上图的第2种情况
cpp
class Solution {
public:
int findMin(vector<int>& nums) {
int left = 0, right = nums.size() - 1;
while(left < right)
{
int mid = left + (right - left) / 2;
if(nums[mid] >= nums[0]) left = mid + 1;
else right = mid;
}
if(nums[nums.size() - 1] > nums[0]) return nums[0];
return nums[left];
}
};