[c语言]个人总结——二分算法

前言:最近重新拾起尘灰已久的算法,在复习二分算法的时候想起来,以前总是对二分算法一知半解,处于会用但不会变通,总的来说就是对于二分算法了解的不够深入。而这一次从新学习这个算法,看了不少文章,感觉有了些许收获,因此想将其记下来,一是巩固,二是想通过大家敏锐的目光,对我的理解进行查漏补缺

一 核心思路

在学习过程中,有一个思路对我帮助很大:"二分算法的核心就是,在每次二分的过程中,将数组分成两部分,一部分是肯定不包含目标数字,一部分是有可能包含目标数字",这句话在对于如果目标数有很多个的时候很有用,也是我这次主要想讨论的。

二 浅识二分

通常在初学二分的过程中,首先学到的就是解决"如何通过二分,在一个严格递增的数组中找到目标数"或者是"如何通过

re

二分,找到一个递增数组中第一个出现的目标数"这两个问题,紧接着就会学习到第一个二分模板

int l = 0,r = n - 1;
while(l < r)
{
    int mid = l + r >> 1;
    if(num[mid] > x)l = mid + 1;
    else r = mid;       
}
if(num[l] == x)
return l;

先解释一下这个二分模板吧,首先解释一下为什么要 l = mid + 1,先说明一个特殊情况,那就是当目标数组出现在一个数组的最末端时,在'循环以l < r为结束条件'和'mid = l + r >> 1' 这两种情况下,只有这样才不会产生死循环,不理解的话给大家出个题

题目为"上图是已经经过不断二分,剩余的三个数,l在7号位,r在9号位,目标数是5,请进行二分",大家可以进行尝试,就能发现只有这种l = mid + 1的情况下才会不发生死循环,而之所以要让r = mid,是因为保证如果数组中有多个目标数,那么最后选的数一定为第一个出现的数。

三.二分扩展

上面的二分模板很有用,但是有一个二分,是这个模板没办法解决的,那就是"在递增数组中找到最后一个出现的目标数"。

初见这个问题的时候,可以通过上面的模板联想到,既然「l = mid + 1,r = mid」可以找到第一个出现的目标数,那么可不可以通过「l = mid,r = mid - 1」来找到最后一个出现的目标数呢,答案是思路正确,但如果目标数出现在最后的话,就不行。原因很简单,大家可以用上面给大家出的题再来试试,就会发现,l 始终会在8这个位置不断循环,而循环的原因就是,当奇数除不尽时,不是四舍五入,二是舍弃余数,所以当我们向数组的末端不断逼近时,必定会出现上面给出的情况,会导致二分陷入死循环。

四.解决方法

「1」通过我们上面的分析可以知道,之所以无法使用「l = mid,r = mid - 1」,就是因为会出现联系两次的mid相同,那我们把它变成不同就可以解决了,解决方法就是:

将mid = (l + r )>> 1,变成mid = (l + r + 1)>>1

或许可能会想,这个加一不会影响二分吗,答案是不会的。我们可以把(l + r + 1) 看成{l + (r + 1)},这个的解释实际上就是把原数组长度加一后进行二分,但是只是看作,实际上并没有加一。这样做就可以保证不会出现联系两个Mid相同的情况

「2」下面是第二种方法,讲的很好,有时间的话再把我的理解附上http://t.csdnimg.cn/AuhJ5icon-default.png?t=N7T8http://t.csdnimg.cn/AuhJ5

五,模板总结(来自算法基础班闫总的模板)

bool check(int x) {/* ... */} // 检查x是否满足某种性质

// 区间[l, r]被划分成[l, mid]和[mid + 1, r]时使用:
int bsearch_1(int l, int r)
{
    while (l < r)
    {
        int mid = l + r >> 1;
        if (check(mid)) r = mid;    // check()判断mid是否满足性质
        else l = mid + 1;
    }
    return l;
}
// 区间[l, r]被划分成[l, mid - 1]和[mid, r]时使用:
int bsearch_2(int l, int r)
{
    while (l < r)
    {
        int mid = l + r + 1 >> 1;
        if (check(mid)) l = mid;
        else r = mid - 1;
    }
    return l;
}

作者:yxc
链接:https://www.acwing.com/blog/content/277/
来源:AcWing

如果对上面两种区域的划分不会的话,可以参考上文进行理解,如果有错误,还望多指正

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值