一种整数集上二分的正确写法

(参考:李煜东《算法竞赛进阶指南》:0x04 二分)

我们都知道,实数域上的二分的写法非常简单,确定好精度很省心。

而整数集上的二分,是需要关注起始边界终止边界中点选择左右区间取舍时的开闭情况的。

《算法竞赛进阶指南》上的整数集合上二分写法和我平时喜欢使用的是一样的,因此进行记录,以便后续参考。

本文所记录的二分写法,需要保证答案处于闭区间 $[l,r]$ 内、循环以 $l=r$ 结束、每次二分的中点 $mid$ 会归属于左右区间中的一个。

(1)在单调递增序列 $a$ 中查找,第一个满足大于等于 $x$ 的数:

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

(2)在单调递增序列 $a$ 中查找,最后一个满足小于等于 $x$ 的数:

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

(注:在计算 $mid$ 使用算术右移而非除法,是因为算术右移的结果始终是向下取整,而除法是向零取整,使用算术右移可以使得区间为负数时也可以正常二分。若我们能保证在非负整数集合上二分,除 $2$ 也是可以的。)

另外,我们注意到:

对于写法(1), mid=(l+r)>>1 不会取到 $r$。因此,若我们将初始的二分范围 $[l,r]$ 扩大到 $[l,r+1)$,一旦二分无解,则最终返回的下标将会是 $r+1$。

对于写法(2), mid=(l+r+1)>>1 不会取到 $l$。因此,若我们将初始的二分范围 $[l,r]$ 扩大到 $(l-1,r]$,一旦二分无解,则最终返回的下标将会是 $l-1$。

这样一来,便可以轻松地判断二分是否有解。

猜你喜欢

转载自www.cnblogs.com/dilthey/p/9903668.html
今日推荐