排除法二分(减治思想)
将目标区域分为两段,对边界进行判断。对边界的判断只区分两种情况,true与false。


根据对称性,mid = left + (right -left + 1) / 2 为什么不会变成取不到左边界呢,因为 mid 的左半区是通过 mid = mid - 1 来更新。
当然可以反过来,将判断结果改为<=,右半区改为 mid = mid + 1 来更新
二分的本质
二分的本质是边界:
假设给定我们一个区间,我们在这个区间上定义了某种性质:使得在右半边区间是满足这个性质的,左半边区间是不满足这个性质的,(两个区间是没有交点的,由于是整数二分),整个区间可以被一分为二。
如果可以找到这样一个性质,使得我们可以把整个区间一分为二,一半满足,一半不满足,二分就可以寻找这个性质的边界。既可以寻找绿色点的边界也可以寻找红色点的边界(二分绿色点和红色点也就是两个不同的模板)

假设想二分出红色边界点的话,应该怎么二分?
①找一个中间值:mid = (l + r + 1) / 2,每次判断中间值是不是满足这个性质:if check(mid) 是不是满足红色这个性质
②如果 check(mid) 为 true,说明 mid 是满足这个条件的,mid 一定在红色这个区间里面,答案所在的区间就是 [ mid,r ](由于可以取到边界点,因此 mid 可能是答案),更新方式是把 [ l,r ] 这个区间更新成 [ mid,r ] 这个区间,更新方式只需要写 l = mid 即可,由于 r 不变
③当 mid 不满足红色性质的时候,mid 一定取在绿色的地方,答案一定是在 [l,mid - 1] 里面,由于 mid 一定是不满足红色的性质的,答案边界一定不在 mid 上,最多在 mid - 1 的位置,更新方式是把 [ l,r ] 这个区间更新成 [ l,mid - 1 ] 这个区间,更新方式只需要写 r = mid - 1 即可,由于 l 不变
假设想二分出绿色边界点的话,应该怎么二分?
①找一个中间值:mid = (l + r) / 2,每次判断中间值是不是满足这个性质:if check(mid) 是不是满足绿色这个性质
②如果 check(mid) 为 true,说明 mid 是满足这个条件的,mid 一定在绿色这个区间里面,答案所在的区间就是 [ l,mid ](由于可以取到边界点,因此 mid 可能是答案,边界点可能在 mid 上)
③当 mid 不满足绿色性质的时候,mid 一定取在红色的地方,答案在 [mid + 1,r] 里面,由于 mid 一定是不满足绿色的性质的,答案边界一定不在 mid 上,最多在 mid + 1 的位置
参考
https://www.bilibili.com/video/BV147411i7zu?vd_source=6c2daed6731190bb7d70296d6b9746bb
35. 搜索插入位置
给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引。如果目标值不存在于数组中,返回它将会被按顺序插入的位置。
请必须使用时间复杂度为 O(log n) 的算法。
示例 1:
输入: nums = [1,3,5,6], target = 5
输出: 2
示例 2:
输入: nums = [1,3,5,6], target = 2
输出: 1
示例 3:
输入: nums = [1,3,5,6], target = 7
输出: 4
// 相当于找第一个大于目标的数的下标
class Solution {
public:
int searchInsert(vector<int>& nums, int target) {
int n = nums.size();
int left = 0, right = n - 1;
while (left < right) {
int mid = (left + right) / 2;
if(target <= nums[mid]) {
right = mid;
} else {
left = mid + 1;
}
}
if(nums[left] < target) {
return left + 1;
}
return left;
}
};
lc 34. 在排序数组中查找元素的第一个和最后一个位置(二分的本质是边界)
https://blog.csdn.net/weixin_60569662/article/details/124901559
class Solution {
public:
vector<int> searchRange(vector<int>& nums, int target) {
if (nums.empty()) return {-1, -1};
int first = binarySearchFirst(nums, target);
int last = binarySearchLast(nums, target);
vector<int> result;
return {first,last};
}
// 将 [first target, right] 作为 check为真的区间。找出第一个target
int binarySearchFirst(vector<int>& nums, int target) {
int n = nums.size();
int left = 0, right = n - 1;
while (left < right) {
int mid = left + (right - left) / 2;
if(nums[mid] >= target) {
right = mid;
} else {
left = mid + 1;
}
}
if(nums[left] == target)
return left;
return -1;
}
// // 将 [left,last target] 作为 check为真的区间。找出最后一个target
int binarySearchLast(vector<int>& nums, int target) {
int n = nums.size();
int left = 0, right = n - 1;
while (left < right) {
int mid = left + (right - left+1) / 2;
if(nums[mid] <= target) {
left = mid;
} else {
right = mid - 1;
}
}
if(nums[left] == target)
return left;
return -1;
}
};

浙公网安备 33010602011771号