1. 程式人生 > >how to define boundary in binary search 二分法的邊界設定

how to define boundary in binary search 二分法的邊界設定

用虛擬碼來表示, 二分查詢演算法大致是這個樣子的:

left = 0, right = n -1
while (left <= right)
    mid = (left + right) / 2
    case
        x[mid] < t:    left = mid + 1;
        x[mid] = t:    p = mid; break;
        x[mid] > t:    right = mid -1;

return -1;

邊界錯誤造成的問題:
1, miss one item (右邊界:R < n or <= n - 1)
二分查詢演算法的邊界,一般來說分兩種情況,一種是左閉右開區間,類似於[left, right),一種是左閉右閉區間,類似於[left, right]. 需要注意的是, 迴圈體外的初始化條件,與迴圈體內的迭代步驟, 都必須遵守一致的區間規則,也就是說,如果迴圈體初始化時,是以左閉右開區間為邊界的,那麼迴圈體內部的迭代也應該如此.
如果兩者不一致,
在迴圈初始化的時候,初始化right=n,也就是採用的是左閉右開區間,而當滿足array[middle] > v的條件是, v如果存在的話應該在[left, middle)區間中,但是這裡卻把right賦值為middle - 1了,這樣,如果恰巧middle-1就是查詢的元素,那麼就會找不到這個元素.
比如下面就是錯誤的二分查詢演算法:

int search_bad(int array[], int n, int v)
{
    int left, right, middle;

    left = 0, right = n;

    while (left < right)
    {
        middle = (left + right) / 2;
        if (array[middle] > v)
        {
            right = middle - 1;
        }
        else if (array[middle] < v)
        {
            left = middle + 1;
        }
        else
        {
            return middle;
        }
    }

    return -1;
}

2, 死迴圈 (左邊界:L = mid or mid + 1)
把上例中的L=mid+1改成L=mid那就很可能會死迴圈。因為使用mid = (L+R)/2這種計算方式的話,當R-L=1時,mid是等於L的。而此時如果恰好執行了L=mid,那就意味著在這次iteration中,L的值沒有變化,即搜尋範圍沒有變,於是就死迴圈了。

正解:
下面給出兩個演算法, 分別是正確的左閉右閉和左閉右開區間演算法,可以與上面的進行比較:

int search2(int array[], int n, int v)
{
    int left, right, middle;

    left = 0, right = n - 1;

    while (left <= right)
    {
        middle = (left + right) / 2;
        if (array[middle] > v)
        {
            right = middle - 1;
        }
        else if (array[middle] < v)
        {
            left = middle + 1;
        }
        else
        {
            return middle;
        }
    }

    return -1;
}

int search3(int array[], int n, int v)
{
    int left, right, middle;

    left = 0, right = n;

    while (left < right)
    {
        middle = (left + right) / 2;

        if (array[middle] > v)
        {
            right = middle;
        }
        else if (array[middle] < v)
        {
            left = middle + 1;
        }
        else
        {
            return middle;
        }
    }

    return -1;
}