1. 程式人生 > >C++左閉右開區間的理解

C++左閉右開區間的理解

C++的區間是左閉右開的,關於這樣做的優勢,做了一個筆記整理,也處理下之前一直比較模糊的區間二分的問題。

左閉右開的區間第一個優勢是,當需要取中間元素的時候,mid=begin+end/2的定位問題。如果區間元素的個數是奇數個,那麼mid永遠是指向中間的元素;如果區間元素是偶數個,那麼mid永遠指向後半段區間的首元素。這樣做在二分查詢等一些演算法的實現上特別有優勢。mid的另一個等效的寫法是mid=begin+(end-begin)/2

比如區間的下標是0,1,2,3,是偶數個,那麼begin=0,end=4,所以mid=(begin+end)/2=(0+4)/2=2,正好是後半段首元素。再看區間下標是0,1,2,3,4

,是奇數個,那麼begin=0,end=5,所以mid=(begin+end)/2=(0+5)/2=2,此時2正好是中間的元素。
在任意合理的區間[begin,end)上,總是有mid=(begin+end)/2把區間分成[begin,begin+mid)[mid,end)兩個部分。

第二個優勢在於方便迭代器快速的進行終止判別。使用左閉右開的區間,迭代終止的條件是begin==end(或者begin>=end),這樣僅需要一個條件就能終止迭代判斷。

第三個優勢在於快速統計區間元素的個數,n=end-begin即為元素的個數。

上述兩個優勢對於特殊情況,只有一個或者兩個元素的區間(這一般發生在二分之類的演算法快要終止的時候),也有更好的效果。如果當前只剩下一個元素,位置是index

,則所在的區間是[index,index+1),因為這是奇數個元素,所以mid=(2*index+1)/2=index,所以前半個區間是mid元素,後半段區間是空。如果有兩個元素,[index,index+1],那麼mid=(2*index+2)/2=index+1,正好把區間分成兩個元素,又回到了僅有一個元素的情況了。

根據上述的描述,可以更好的理解二分演算法了:

#include <iostream>
using std::cout;
using std::cin;
using std::endl;

int BinarySearch(int Arr[], int first, int last, int val) {
    while(first != last) {      // 終止條件
        int mid = first + (last - first) / 2;  // 區間二分
        // int mid = (first + last) / 2;       // 另一種等效的寫法
        if(Arr[mid] == val) {   // 找到
            return mid;
        } else if(mid > val) {  // 左側區間
            last = mid;
        } else {                // 右側區間
            first = mid + 1;
        }
    }
    return -1; // 沒找到
}

int main() {

    int A1[5] = {0, 1, 2, 3, 4};
    cout << "--------------測試奇數個元素----------------\n";
    for(int i = 0; i < 5; ++i) {
        cout << BinarySearch(A1, 0, 5, i) << endl;
    }
    cout << BinarySearch(A1, 0, 4, -1) << endl;
    cout << BinarySearch(A1, 0, 4, 5) << endl;

    int A2[5] = {0, 1, 2, 3};
    cout << "--------------測試偶數個元素----------------\n";
    for(int i = 0; i < 5; ++i) {
        cout << BinarySearch(A2, 0, 4, i) << endl;
    }
    cout << BinarySearch(A2, 0, 4, -1) << endl;
    cout << BinarySearch(A2, 0, 4, 4) << endl;

    return 0;
}