排除法二分(减治思想)

将目标区域分为两段,对边界进行判断。对边界的判断只区分两种情况,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;
    }
};
posted @ 2025-03-19 15:06  丘狸尾  阅读(24)  评论(0)    收藏  举报