二分查找(二分答案)套路模板

二分查找 红蓝染色法【基础算法精讲 04】_哔哩哔哩_bilibili

提要

二分答案是一种高效的算法策略,适用于解决最值问题单调性验证问题。它的核心思路是将问题转化为对答案的判定过程,通过二分法快速缩小搜索范围

笔者写的都是以左闭右开区间为主的

下面笔者会按照一道题的做题顺序来分析步骤,建议你最好先做一下或者起码要把题读了再往下读

875. 爱吃香蕉的珂珂 - 力扣(LeetCode)

Day69 | 灵神 | 二分查找:爱吃香蕉的珂珂-CSDN博客

题解

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
class Solution {
public:
bool check(vector<int>& piles,int k,int h)
{
long long sum=0;
for(auto c:piles)、
{
if(c%k==0)
sum+=(c/k);
else
sum+=(c/k)+1;
}
return sum<=h?true:false;
}
int minEatingSpeed(vector<int>& piles, int h) {
int l=1,r=0;
for(auto c:piles)
r=max(r,c)+1;
while(l<r)
{
int mid=l+(r-l)/2;
if(check(piles,mid,h))
r=mid;
else
l=mid+1;
}
return r;
}
};

步骤

笔者认为解题难点一般都在check函数如何设计和处理

1.看单调性并找要二分什么

拿到一道题我们如何判断这道题可以使用二分查找?看单调性

即当答案增大或减小时,问题的答案呈现单调变化,有点抽象,看看在本题中代表着什么呢?

在本题中

看示例 1,piles=[3,6,7,11], h=8

如果珂珂能用 k=4 的速度吃掉所有香蕉,那么也能用更快的速度 k=5,6,⋯ 吃掉所有香蕉

如果珂珂不能用 k=3 的速度吃掉所有香蕉,那么也不能用更慢的速度 k=2,1,⋯ 吃掉所有香蕉

这个单调性就说明了我们可以使用二分查找找到答案

那我们怎么知道要看k的单调性呢?

因为这是题目让我们求的东西,二分答案,那肯定是二分这道题让求的东西

所以一般二分什么呢?二分的就是题目让求的

2.找二分区间,即确定搜索范围

即找答案所在的区间,即我们要二分的真正的区间,根据题目条件找答案的上界和下界

在本题中,

题目说piles的大小小于时间h,这使得我们很容易找到k的最大值,那就是数组的最大值,我每个小时吃这么多肯定可以吃得完,而最小值肯定不能是0,因为怎么都不可能吃完,所以从1开始

那么我们要二分的k的区间就是[1,数组最大值],在第一个例子中就是[1,11]

笔者用的左闭右开区间,那就是[1,数组最大值)

找到之后就可以写模板上去了,例如区间是左闭右开[left,right)

1
2
3
4
5
6
7
8
9
10
11
int l=left,r=right;
//如果区间是[left,right],那么r=right+1
while(l<r)
{
int mid=l+(r-l)/2;
if(check())

else

}
return ;

3.找判断条件,即写验证函数check()

编写 check(mid) 函数,判断当前 mid 是否满足条件,然后转变思维把mid当成一个定值,思考check函数到底要解决一个什么样的实际问题

在本题中,要解决的问题变成:(这个很关键,碰到难题的时候能不能想到这里决定能不能解题)

  • 判断珂珂能否用 k 的速度,在 h 小时内吃掉所有香蕉。

我们二分的是k,是速度,那要如何判断一个k是否满足条件?那就看这个k可以在多少小时(sum)内吃完香蕉,如果这个sum小于等于h,那肯定就是符合条件的k,那么[k,数组最大值]这个区间肯定都符合条件,然后继续往左收缩就是了,如果不符合条件的k的话,那说明吃得太慢了,那就往右收缩

1
2
3
4
5
6
7
8
9
10
11
12
bool check(vector<int>& piles,int k,int h)
{
long long sum=0;
for(auto c:piles)、
{
if(c%k==0)
sum+=(c/k);
else
sum+=(c/k)+1;
}
return sum<=h;
}

能整除就直接加,不能整除就+1,算出时间

4.满足条件更新l还是r

更新l还是r就看你的check函数怎么写了

本题就看sum是大于还是小于h了,本题是sum小于等于h,找的是左边界,只要mid满足条件,那么大于mid的肯定都满足条件,所以更新r,这是顺向的思维,你要非写成sum>h返回true的话也不是不行

5.返回答案的问题

为什么要把返回答案的问题拿出来说呢?

假设一开始的区间是[left,right)

本道题更新的是r,说明如果符合条件,那么[r,right)肯定都符合条件

而最后退出循环是l==r的时候退出,所以最后返回l或者返回r都是最后答案

但是在更新的是l的时候呢?比如这题Day73 | 灵神 | 二分查找 最大合金数-CSDN博客,即找最大值,即从左往右收缩的时候

更新l时,更新的是l=mid+1,但是满足条件的是mid,而不是mid+1,很有可能mid+1就不符合条件,如果mid+1符合条件,那么l和r一定会更新到这里并且循环不会结束的

如果是在最后一次循环结束后即l=mid+1之后,使得l==r导致了循环退出,那么其实mid+1并不符合条件

所以如果满足check函数之后更新的是l=mid+1,最后需要返回l-1,而不是l

6.适用的题型以及条件要求

现在说这个估计大家就能更好的看懂的,如果放在开头感觉有点云里雾里

使用场景

最值问题:如“最大值最小化”(如最小化最大子段和)或“最小值最大化”(如最大化最小间隔),这个东西笔者学的也不求行,就不多说了

单调性问题:当答案增大或减小时,问题的可行性呈现单调变化(如更大的间隔可能导致更少的可行解),这个就是我举例的那道题的类型

条件要求

有界性:答案必须存在于一个确定的区间内(例如整数范围 [0, 1e9]),即我们的二分区间应该有明确的上界和下界

单调性:验证函数需满足单调性,即若答案 mid 可行,则所有更小(或更大)的值也一定可行(取决于问题方向),这个就是我们上面说的单调性,大家通过这个题目应该很好的理解了

笔者的关于灵神作业的题解

Day65 | 灵神 | 二分查找:红蓝染色法-CSDN博客

Day66 | 灵神 | 二分查找:咒语和药水的成功对数-CSDN博客

Day67 | 灵神 | 二分查找:统计公平数对的数目-CSDN博客

Day68 | 灵神 | 二分查找:H指数II-CSDN博客

Day69 | 灵神 | 二分查找:爱吃香蕉的珂珂-CSDN博客

Day70 | 灵神 | 二分查找:完成旅途的最少时间-CSDN博客

Day71 | 灵神 | 二分查找 最小化数组中的最大值-CSDN博客

Day72 | 灵神 | 二分查找 礼盒的最大甜蜜度-CSDN博客

Day73 | 灵神 | 二分查找 最大合金数-CSDN博客

灵神的题单

更多的题在这里,感兴趣的读者可以自行选择

分享丨【题单】二分算法(二分答案/最小化最大值/最大化最小值/第K小)- 讨论 - 力扣(LeetCode)