二分
(https://programmercarl.com/0704.%E4%BA%8C%E5%88%86%E6%9F%A5%E6%89%BE.html)
二分模板
注意二分可以有左闭右开,和左闭右闭的两种写法
这里用左闭右开,即l<r
左边界:区间[l, r]被划分成[l, mid]和[mid + 1, r]时使用:
while (l < r)
{
int mid = l + r >> 1; //(l+r)/2
if (check(mid)) r = mid; // check()判断mid是否满足性质
else l = mid + 1;
}
右边界:区间[l, r]被划分成[l, mid - 1]和[mid, r]时使用:
while (l < r)
{
int mid = l + r + 1 >> 1; //(l+r+1)/2
if (check(mid)) l = mid;
else r = mid - 1;
}
如下图:左右边界指的是____________左边界 右边界____________
记忆: 有加必有减
int mid = l + r + 1 (加)>> 1;
if (check(mid)) l = mid;
else r = mid - 1 (减);
来自y总的草稿!
思路:
有单调一定可二分,二分不一定要单调,只要满足整个区间可以根据某一性质,将区间一分为二,一边满足该性质一边不满足即可;
我们既可以找红色区间的右边界点,也可以找绿色区间的左边界点,取决于你check(mid)是用的红色性质还是绿色性质;
红色性质和绿色性质区间是互斥的,一边满足一边就不满足;
步骤:
- 先根据题目划分区间,写出check(mid)函数,这里的check(mid)可以是红色性质区间也可以是绿色性质区间;
- 根据想找的边界点,将区间划分为[l, mid]和[mid + 1, r]则令r=mid,l=mid+1,或者[l, mid - 1]和[mid, r]则令r=mid-1,l=mid
- 如果代码中有l=mid时,应该要mid=(1+l+r)>>1;
ps:写多了就会发现,当用check(mid)==true,如果是令l=mid时,则前面要加1。
一般二分应用于无非下面这四种情况:
- 找大于等于数的第一个位置 (满足某个条件的第一个数)
- 找小于等于数的最后一个数 (满足某个条件的最后一个数)
- 查找最大值 (满足该边界的右边界)
- 查找最小值 (满足该边界的左边界)
板子题:[力扣34](34. 在排序数组中查找元素的第一个和最后一个位置 - 力扣(LeetCode))
//这是左闭右闭写法,l往右走,r往左走
//找右边界,让l=mid+1,即往右,不仅nums[mid]<target更新l往右,而且==时也要更新往右,才能找到右边界
//左边界同理,让r=mid-1,即往左走,不仅nums[mid]>target更新r往左,而且==时也要更新往左,才能找到左边界
class Solution {
public int[] searchRange(int[] nums, int target) {
int leftBorder=getLeftBorder(nums,target);
int rightBorder=getRightBorder(nums,target);
if(leftBorder==-2||rightBorder==-2) return new int[]{-1,-1};
if(rightBorder-leftBorder>=2) return new int[]{leftBorder+1,rightBorder-1};
return new int[]{-1,-1};
}
int getRightBorder(int[] nums,int target)
{
int l=0,r=nums.length-1;
int rightBorder=-2;
while(l<=r)
{
int mid=l+((r-l)>>1);
//找右边界,不仅nums[mid]<target更新l往右,而且==时也要更新往右
//l往右走,r往左走
if(nums[mid]<=target){
l=mid+1;
rightBorder=l;
}
else r=mid-1;
}
return rightBorder;
}
int getLeftBorder(int[] nums,int target)
{
int l=0,r=nums.length-1;
int leftBorder=-2;
while(l<=r)
{
int mid=l+((r-l)>>1);
//找左边界,不仅nums[mid]>target更新r往左,而且==时也要更新往左
if(nums[mid]>=target){
r=mid-1;
leftBorder=r;
}
else l=mid+1;
}
return leftBorder;
}