二分模板笔记
在排序数组中查找元素的第一个和最后一个位置
给定一个按照升序排列的整数数组 nums,和一个目标值 target。找出给定目标值在数组中的开始位置和结束位置。
如果数组中不存在目标值 target,返回 [-1, -1]。
示例 1:
输入:nums = [5,7,7,8,8,10], target = 8 输出:[3,4]
二分的本质是二段性不是单调性。
当想找不满足性质的边界值(红色区域的右边界值)
找中间值 mid = (l+r+1)/2 if(check(mid))等于true或者是false check(m)是检查m是在不满足性质的区间(检查是不是在红色区间) 更新l或者r
当想找满足性质的边界值(绿色区域的左边界值)
- 找中间值 mid = (l+r)/2
- if(check(mid))等于true或者是false check(m)是检查m是在满足性质的区间(检查是不是在绿色区间)
- 更新l或者r
归结上面的两种二分方法,步骤为:
先写一个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
这种方法保证了:
- 最后的l==r
- 搜索到达的答案是闭区间的,即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;
};