二分的几个模板及解释

二分的几个模板及解释

1.查找≥x的数中最小的一个

在单调递增序列a中查找≥x的数中最小的一个(即x或x的后继)

    while (l < r)
    {
        int mid = (l + r) >> 1;
        if (a[mid] >= x)
            r = mid;
        else
            l = mid + 1;
    }
    return l;

在这个代码中,我们以l和r相等的时候作为终止条件,每次选取中间的数进行判断,如果该数字大于等于目标数,那么答案一定在[l,mid],因为mid右侧全是大于等于x的;如果该数小于x,那么答案一定在[mid+1,r],因为mid及mid左侧全部都小于x,所以只能在右侧。在这个代码里面,我们始终保持答案在[l,r]里面。

也有其他的写法

    int l = 0, r = n;
    while(l+1!=r)
    {
        int mid = (l + r) / 2;
        if(a[mid]>=x)
            r = mid;
        else
            l = mid;
    }
    return r; 

这里我们从1开始存储,我们的终止条件是l和r相邻。因为l和r都是直接取mid,这样我们能一直保证答案在(l,r],只有a[mid]<x的情况下,l才取值为mid,所以左区间是开的,因为l的初值为0,所以可以从一开始的情况下就保证答案在(l,r]。

或者可以直接用STL的写法,lower_bound是直接返回第一个大于等于x的位置

int num=lower_bound(a+1,a+n+1,x)-a;
return num;

2.查找≤x的最大的一个

在单调递增序列a中查找≤x的数中最大的一个(即x或
x的前驱)

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

如果a[mid]≤x,那么答案就在[mid,r],因为mid左边的更小,如果a[mid]>x,那么答案就在[l,mid-1]。mid之所以取(l+r+1)/2,因为只剩下两个数的时候会取第一个,如果a[l]小于等于x,那么l=mid会死循环。

看第二种写法

    int l = 1, r = n+1;
    while(l+1!=r)
    {
    	int mid=(l+r)/2;
    	if(a[mid]<=x)l=mid;
    	else r=mid;
	}
	return l;

这里我们始终保持答案在区间[l,r)之间,当最后只剩下两个数,即l和r相邻的时候,l就是答案。

我为什么在(1)和(2)中都没有给a[0]或者a[n+1]赋初值,是因为这两个都不会被取到,就比如在(1)中,如果只剩下0 1 2这三个数,那么我们会对1进行判定,如果结果只剩0 1,我们会直接输出1,1 2同理。在(2)中也同理。
如果不考虑时间和空间的话,我们可以直接l=0,r=1e9,然后给a[0]赋一个极小值,给r[n]之后全部赋极大值,这样最终也会找到答案。

同理我们依然可以使用STL的方法来解决问题。

    int num=upper_bound(a+1,a+n+1,x)-a;
    return num-1;

因为upper_bound是返回大于x的最小的那一个,那么他前一个就是小于等于x了(当然不嫌麻烦还是可以用lower_bound加上一个判断来做)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值