二分搜的實作方式

常見

各種二分搜的模式

如果是常常閱讀程式碼的人,在閱讀二分搜的時候肯定都遇過一個問題 : 為什麼每個人維護的 leftleftrightright 都不太一樣,有時候是 left=midleft = mid,有時候是 left=mid+1left = mid + 1,這其中又有什麼不同呢 ?

開區間與閉區間

在決定 leftleftmidmid 要怎麼維護之前,我們要先理解開區間和閉區間是什麼。

開區間

開區間指的是區間的邊界不被包含在區間內,符號上會表示成 (a,b)(a, b),集合內的點 xx 符合條件 a<x<ba < x < b

閉區間

開區間指的是區間的邊界不被包含在區間內,符號上會表示成 [a,b][a, b],集合內的點 xx 符合條件 axba \le x \le b

二分搜終止條件

前面討論的區間就是為了定義二分搜的終止條件,以及如何維護 midmid 的值。首先,我們要先決定 leftleftrightright 分別代表的意義,以左閉右開的二分搜舉例,假設二分搜的目標是找到 xx,既然是使用左閉右開的二分搜,所以我們要維護兩個區間 [left,mid)[left,mid)[mid,right)[mid,right),一直到區間最後變成 [x,x)[x, x),因為這個區間會剛好只有 xx 一個元素或是完全沒有元素,換句話說維護到最後區間的內容可以被解讀為找到 xx 或是沒找到 xx,也就是說 while 迴圈的條件為 while(left < right) 即可讓區間停在左閉右開的 [x,x)[x, x),並且最後的答案會是 leftleft

各種二分搜的範例程式碼

左閉右閉

// 假設陣列 array 有 n 個元素
// 左閉右閉最後維護的值為 [x, x]
int left = 0, right = n - 1;
bool check = false;
while (left <= right) {
	int mid = (left + right) / 2;
	if (array[mid] < key) {
		left = mid + 1;
    }
	else if (array[mid] > key) {
		right = mid - 1;
    }
	else if (array[mid] == key) {
		cout << "答案在第" << mid << "項";
        check = true;
        break;
    }
}
if (check == false) {
    cout << "答案不在陣列中";
}

左閉右開

// 假設陣列 array 有 n 個元素
// 左閉右閉最後維護的值為 [x, x)
int left = 0, right = n - 1;
bool check = false;
while (left <= right) {
	int mid = (left + right) / 2;
	if (array[mid] < key) {
		left = mid + 1;
    }
	else if (array[mid] > key) {
		right = mid - 1;
    }
	else if (array[mid] == key) {
		cout << "答案在第" << mid << "項";
        check = true;
        break;
    }
}
if (check == false) {
    cout << "答案不在陣列中";
}

半開半閉 (左開右開)

使用注意

因為答案可能是 (x,x](x, x] 也可能是 [x+1,x+1)[x + 1, x + 1),使用者須根據情況決定使用 leftleft 或是 rightright

// 假設陣列 array 有 n 個元素
// 左閉右閉最後維護的值為 (x, x)
int left = -1, right = n;
bool find = false;
while (left + 1 < right) {
    int mid = (left + right) / 2;
    if(ans == array[mid]) {
        cout << "答案在第" << mid << "項";
        find = true;
        break;
    }
    else if(ans < array[mid]) {
        right = mid;
    }
    else {
        left = mid;
    }
}
if (find == false) {
    cout << "答案不在陣列中";
}