二分查找总结

二分搜索?莫非就是对于一个值单调递增的序列,给出\(l,r\),将要找的值与区间\([l,r]\)的中点值\(a[m]\)比较,大了\(l\)变成\(m\),小了\(r\)变成\(m\)呗?但是如果不细究,很容易发生off one错误。下面我们将要讨论该问题。

序列内没有重复数字

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

因为没有重复数字,所以如果mid的值就是所求,返回mid就可以,否则既然mid的值无效了,范围缩小后的区间就不包括mid了。

LowerBound和UpperBound

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

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

定义

LowerBound:给出\(l,r,x\),通过给出\(p\)的形式,求得一个区间\([p,r]\),使得任意一个下标\(k\),\(k\in [p,r]\Leftrightarrow a_k\geq x\)
UpperBound:给出\(l,r,x\),通过给出\(p\)的形式,求得一个区间\([l,p]\),使得任意一个下标\(k\),\(k\in [l,p]\Leftrightarrow a_k\leq x\)
\(m=\frac{l+r}{2}\)
左区间:\([l,\lfloor m\rfloor]\),右区间:\([\lceil m\rceil,r]\)
在LowerBound代码中,mid为\(\lfloor m\rfloor\);在UpperBound代码中,mid为\(\lceil m\rceil\)

原理

看看两个代码中mid的定义,发现无论是UpperBound,还是LowerBound,其缩小区间时,要么缩小到左区间,要么缩小到右区间。

序列中存在\(a_k=x\)

这意味着\(k\)的值可能有多个。

\(r-l+1\)为偶数

这意味着\(m\notin \mathbb{Z}\)。如果\(a_{\lfloor m\rfloor},a_{\lceil m\rceil}\)中存在不多于1个与\(x\)相等,这简单,若\(x\geq a_{\lceil m\rceil}\),则转移到右区间;若\(x\leq a_{\lfloor m\rfloor}\),则转移到左区间。两个代码都满足这个要求。
如果\(a_{\lfloor m\rfloor},a_{\lceil m\rceil}\)两个值都与\(x\)相等,则因为LowerBound要的\(k\)值最小,所以它要舍弃\(a_{\lceil m\rceil}\),在左区间中寻找\(k\leq\lfloor m\rfloor,a_k=x\),故要求转移到左区间。同理UpperBound要求转移到右区间。根据mid的定义以及两段代码在小于等于和大于等于的判定上都满足要求。

\(r-l+1\)为奇数

这意味着\(m\in\mathbb{Z}\)。若\(a_m\neq x\),则若\(x<a_m\),则转移到左区间,否则转移到右区间;
\(a_m=x\),LowerBound要在左区间中寻找\(k\leq\lfloor m\rfloor,a_k=x\),故要求转移到左区间。同理UpperBound要求转移到右区间。根据mid的定义以及两段代码在小于等于和大于等于的判定上都满足要求。

序列中不存在\(a_k=x\)

一开始是普通的二分,最后会遇到这种情况:\(m\notin\mathbb{Z}\)\(a_{\lfloor m\rfloor}<x<a_{\lceil m\rceil}\)。如果是LowerBound,因为\(x>a_{\lfloor m\rfloor}\),故转移到了右区间;UpperBound同理转移到了左区间,正好满足各自的定义。

猜你喜欢

转载自www.cnblogs.com/headboy2002/p/8977085.html