這裏主要討論的是binary search的邊界情況及處理。
參考 : http://my.oschina.net/fullofbull/blog/199693?fromerr=2T6aDmtE
經典版本:(注意,邊界條件迭代、循環終止條件設定,中位數計算)
int binary_search(int *A, int n, int target)
{
int low = 0, high = n - 1;
assert(A != NULL && n >= 0);
while (low <= high)
{
int mid = low + (high - low) / 2;
if(A[mid] == target)
return mid;
else if (A[mid] < target)
low = mid + 1;
else
high = mid - 1;
}
return -1;
}
這裏查找的條件是:
if(A[mid] == target)
return mid;
else if (A[mid] < target)
low = mid + 1;
else
high = mid - 1;
即碰到target就返回,每次進行兩次判斷。
但是判斷
if(A[mid] == target)
在開始時幾乎沒有意義,因爲直接找到的可能性很小,爲1/n。只有在後續的小區間判斷,比較有意義。具體解釋如下《代碼之美》:
我們先來考慮循環的執行步驟。假設我們有一個有着 n 個元素的數組(此處n是一個很大的數值),那麼從該數組中第一次找到目標的概率爲 1/n(一個很小的數值),下一次(經過一次二分)的概率則是 1/(n/2)——仍然不是很大——以此類推下去。事實上,只有當元素的個數減少到了 10 到 20 的時候,一次找到目標的概率才變得有意義,而對於10 到 20 個元素進行查找需要的只是大概 4 次循環。當查找失敗時(在大多數的應用中很普遍),那些額外的測試就將變成純粹的額外開銷。
我們也可以來計算一下,在什麼時候找到目標值的概率能接近 50%,但請你捫心自問:在一個複雜度爲 O(log2N)
的算法中,對於它的每一步都增添一個額外的複雜計算,而目的僅僅是爲了減少最後的幾次計算,這樣做有意義嗎?
所以,查找過程中可以將判斷改爲:
if (A[mid] < target)
low = mid + 1;
else
high = mid;
或者
if (A[mid] > target)
high = mid - 1;
else
low = mid;
這種查找還可以完成查找首個,或者最後一個出現的target。
int binary_search_first_position(int *A, int n, int target)
{
int low = -1, high = n;
assert(A != NULL && n >= 0);
while (low + 1 < high)
{
int mid = low + (high - low) / 2;
if (A[mid] < target)
low = mid;
else
high = mid;
}
if (high >= n || A[high] != target)
return -high - 1;
else
return high; // high == low + 1
}
int binary_search_last_position(int *A, int n, int target)
{
int low = -1, high = n;
assert(A != NULL && n >= 0);
while (low + 1 < high)
{
int mid = low + (high - low) / 2;
if (A[mid] > target)
high = mid;
else
low = mid;
}
if (low < 0 || A[low] != target)
return -low - 2;
else
return low; // low == high - 1
}