二分专题:原视频
特点
二分的题目答案在一个确定的取范 [L,R] 内,所以每次找到取范区间的中点,并通过比较中点与目标值来缩小一般的范围,当 L=R 时,即找到答案。
通常70%的题目是通过单调性(递增/减序列排成一列进行比较);95%的题目通过性质分界点,即题目中的变量存在两端性的性质(找到满足和不满足性质的分界点)
思路
二分题目的思路:
- 确定二分边界
- 编写二分的代码框架
- 设计一个check(性质)
- 判断一下区间如何更新
- 如果更新方式写的是l=mid,r=mid-1,那么就在算mid的时候加上1
模版
二分模版
两个模版都遵循思路,差别在于寻找的点的属于范围不同,注意:每次比较的是中点(用于缩小范围)与端点(判断是哪一侧区间)
注意:每一次的check比较的都是mid与临界点的大小,即mid ? target
//红绿只是二端性的可视表现,与上下界无关。
绿色端点【模版一】 mid下取整
if mid==绿 //说明要找的点【分界点】在mid的左侧,并且mid也可能是分界点
[L,R]->[L,M] //R=M
else if mid是红 //说明要找的点在mid的右侧,并且mid一定不是分界点
[L,R]->[M+1,R] //L=M+1
红色端点【模版二】 mid上取整,由更新方式决定,有50%的可能出现死循环
if mid==红 //说明要找的点【分界点】在mid的右侧,并且mid也可能是分界点
[L,R]->[M,R] //L=M
else if mid是绿 //说明要找的点在mid的左侧,并且mid一定不是分界点
[L,R]->[L,M-1] //R=M-1
题目一
69. x 的平方根
向下取开根,结果一定在 [1,x] 之间,则红绿两段的分界点是根号x设计的答案一定要在两段性的边界上,需要找到红色线段上的黄色点。
- 答案是向下取整,所以临界点应在左侧,更新方式为 l=mid;
class Solution {
public:
int mySqrt(int x) {
//模版二,找红色线段上的黄点
int l=0,r=x; //1.边界
while(l<r) //2.编写框架
{
int mid=l+(long long)r+1>>1; //有可能溢出 所以转化为longlong
if(mid<=x/mid) //3.check //防止两个正数相乘溢出
l=mid; //4.更新
else
r=mid-1;
}return r;
}
};
题目二
- 找的数字应在较大的一侧,即右侧,所以更新方式为r=mid;
//本质就是插入排序,没什么难度
class Solution
{
public:
int searchInsert(vector<int>& nums, int target)
{
//二分
if(nums.empty()||nums.back()<target)
return nums.size();
int l=0,r=nums.size()-1;
while(l<r)
{
int mid=l+r>>