前言:
开个玩笑,我们写算法可不能这样哈~
好了,正片开始:
你是否曾经也有过整数二分因为一直死循环而苦恼,你是否因为搞不清楚整数二分的边界处理而焦躁,明明很简单的一道二分,但是最后就是搞不出来答案来,本文,意在帮助我们在使用二分的时候能够正确的使用边界条件,避免死循环的产生,话不多说,我们开始发车
目录
1.二分问题的一般思路
首先,你的二分为什么会死循环?
mid到底加1还是不加1?
二分问题的模板
2.即学既练
3.金句省身
1.二分问题的一般思路
如果你提前了解过二分的话,应该会知道二分最重要的就是中间值mid和自定义check函数,一般的我们会有如下的模板
while (l < r)
{
int mid = (l + r) >> 1;
if(check(mid))
...
else
...
}
但是,这样固定化的模板可能会让我们在解决实际问题时错误的利用了边界条件导致while循环出现问题而出错,下面我们给出一些反面案例来验证上述模板的不严谨性:
首先,你的二分为什么会死循环?
mid到底加1还是不加1?
我们先来看二分的原理:
我们来看第一种情况,如果我们的序列好巧不巧就只有两个,那我我们就有 l=r-1,那么我们的mid第一次求出来是l,如果恰好满足我们的true的情况,那么就会造成l和r的值永远不会更新,也就会导致死循环的产生,所以,对于第一种情况,我们应该让mid加上一个1,具体实现就是mid=(l+r+1)/2;
所以我们这里大胆的总结出一套二分的模板来帮助大家理解记忆:
二分问题的模板
我们需要根据题目条件判断我们所求的边界点属于满足条件的部分还是不满足条件的部分,也就是要根据所求边界点的不同来判断使用哪种模板来进行二分。
//当我们的区间[l,r]被划分为[l,mid-1]和[mid,r]时使用
int bserch1(int l, int r)
{
while (l < r)
{
int mid = (l + r + 1) / 2;
if (check(mid))
l = mid;
else
r = mid - 1;
}
}
//当区间被划分为[l,mid]和[mid+1,r]时使用
int bserch2(int l, int r)
{
while (l < r)
{
int mid = (l + r) >> 1;
if (check(mid))
r = mid;
else
l = mid + 1;
}
}
2.即学既练
刚学了模板得拿出来练练手啊,别急,给你们准备了题目了,来人呐,上题~~~
典型的二分法,我们首先来分析思路:
1.如何寻找起始点
这里我们要找到一个性质,也就是我们的check函数来帮助我们找到起始点,我们需要把起始点看左边界,那我们可以将a[mid]>=k设为条件,所有大于等于k的元素都会归入右半部分;
2.终止点
终止点的选择和上面的选择同理,我们需要另建一个二分,这次我们需要找到适合判断终止点的check函数来实现,什么样的条件可以让我们判断出终止点的位置呢?其实a[mid]<=k就可以,至于为什么,我画个图就好理解了。
这样就比较好理解了,我们在选择条件时,尽量将满足条件的那一个部分作为一个整体加入一个满足或者不满足的部分,不要将这部分结果分开。
下面我们开始组织代码:
#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e5 + 5;
int n,q;
int a[maxn];
int main()
{
scanf("%d%d", &n, &q);
for (int i = 0; i < n; i++)
scanf("%d", &a[i]);
while (q--)
{
int x;
scanf("%d", &x);
int l1 = 0, r1 = n - 1;
//求起始点
while (l1 < r1)
{
int mid = (l1 + r1) >> 1;//>>1相当于除2,同理,<<1相当于乘2
if (a[mid] >= x)
r1 = mid;//如果满足条件,那么mid一定在右半区域,也就是在起始点的右边或者是起始点上,所以我们最终返回的也是r1
else
l1 = mid + 1;
}
//求终止点
int l2 = 0, r2 = n - 1;
while (l2 < r2)
{
int mid = (l2 + r2+1) >> 1;
if (a[mid] <= x)
l2 = mid;//l2=mid,套模板我们有mid处需要加1,且最终返回l2
else
r2 = mid - 1;
}
//我们有可能找不到结果,所以这里要判断
if (a[r1]==x && a[l2]==x)
printf("%d %d\n", r1, l2);
else
printf("-1 -1\n");
}
return 0;
}
总之,比较核心的就是二分模板的代码,我们可以这样记忆:当有条件l=mid出现时mid计算就要加1。
3.金句省身
你凭什么不努力?
你所羡慕的高学历,经济独立,强大的能力都是别人用尽全力辛苦换来的,当你知道别人付出多少的时候,你可能也就不羡慕别人的生活了,你会发现此刻的你根本不配拥有。醒醒吧,每天幻想无济于事,真正阻碍你变好的是你自己。别再自我纵容,别再顾影自怜,是时候主动努力了。
改变自己就是从逼自己一把开始的。哪有多少天赋异禀,不过是拼尽全力而已。