二分模板笔记

328 阅读2分钟

二分模板笔记

在排序数组中查找元素的第一个和最后一个位置

给定一个按照升序排列的整数数组 nums,和一个目标值 target。找出给定目标值在数组中的开始位置和结束位置。

如果数组中不存在目标值 target,返回 [-1, -1]。

示例 1:

输入:nums = [5,7,7,8,8,10], target = 8 输出:[3,4]

二分的本质是二段性不是单调性。

image.png 当想找不满足性质的边界值(红色区域的右边界值)

找中间值 mid = (l+r+1)/2 if(check(mid))等于true或者是false check(m)是检查m是在不满足性质的区间(检查是不是在红色区间) 更新l或者r

image.png

当想找满足性质的边界值(绿色区域的左边界值)

  1. 找中间值 mid = (l+r)/2
  2. if(check(mid))等于true或者是false check(m)是检查m是在满足性质的区间(检查是不是在绿色区间)
  3. 更新l或者r

image.png

归结上面的两种二分方法,步骤为:

先写一个check函数
判定在check的情况下(true和false的情况下),如何更新区间。
在check(m)==true的分支下是:
l=mid的情况,中间点的更新方式是m=(l+r+1)/2
为什么需要+1?
原因是如果不加上1,那么mid得到的是下取整的数,那么有可能[m,r]更新过后m会一直等于m(m+1==r的情况)会陷入死循环。
r=mid的情况,中间点的更新方式是m=(l+r)/2
这种方法保证了:

  1. 最后的l==r
  2. 搜索到达的答案是闭区间的,即a[l]是满足check()条件的。
var searchRange = function(nums, target) {
    let res = [-1, -1];
    if(nums.length === 0) return res;

    let left =0, right =nums.length-1;
    while(left < right) {
        let mid = left+right >> 1;
        if(nums[mid] >= target) {
            right = mid;
        }else {
            left = mid+1;
        }
    } 
    if(nums[left] != target) return res;
    res[0] = left;

    left =0, right =nums.length-1;
    while(left < right) {
        let mid = left+right+1 >> 1;
        if(nums[mid] <= target) {
            left = mid;
        }else {
            right = mid-1;
        }
    } 
    res[1] = right;
    return res;
};