二分查找(binary search),也稱折半搜索,是一種在 有序數組 中 查找某一特定元素 的搜索算法。搜索過程從數組的中間元素開始,如果中間元素正好是要查找的元素,則搜索過程結束;如果某一特定元素大於或者小於中間元素,則在數組大於或小於中間元素的那一半中查找,而且跟開始一樣從中間元素開始比較。如果在某一步驟數組為空,則代表找不到。這種搜索算法每一次比較都使搜索范圍縮小一半。
- 時間復雜度:折半搜索每次把搜索區域減少一半,時間復雜度為O(log n)。(n代表集合中元素的個數)
- 空間復雜度: O(1)。雖以遞歸形式定義,但是尾遞歸,可改寫為循環。
動圖演示
代碼描述
遞歸
int binarysearch(int array[], int low, int high, int target) {
if (low > high) return -1;
int mid = low + (high - low) / 2;
if (array[mid] > target)
return binarysearch(array, low, mid - 1, target);
if (array[mid] < target)
return binarysearch(array, mid + 1, high, target);
return mid;
}
非遞歸
int bsearchWithoutRecursion(int a[], int key) {
int low = 0;
int high = a.length - 1;
while (low <= high) {
int mid = low + (high - low) / 2;
if (a[mid] > key)
high = mid - 1;
else if (a[mid] < key)
low = mid + 1;
else
return mid;
}
return -1;
}
二分查找中值的計算
這是一個經典的話題,如何計算二分查找中的中值?大家一般給出了兩種計算方法:
- 算法一:
mid = (low + high) / 2
- 算法二:
mid = low + (high – low)/2
乍看起來,算法一簡潔,算法二提取之后,跟算法一沒有什么區別。但是實際上,區別是存在的。算法一的做法,在極端情況下,(low + high)存在着溢出的風險,進而得到錯誤的mid結果,導致程序錯誤。而算法二能夠保證計算出來的mid,一定大於low,小於high,不存在溢出的問題。
二分查找法的缺陷
二分查找法的O(log n)讓它成為十分高效的算法。不過它的缺陷卻也是那么明顯的。就在它的限定之上:必須有序,我們很難保證我們的數組都是有序的。當然可以在構建數組的時候進行排序,可是又落到了第二個瓶頸上:它必須是數組。
數組讀取效率是O(1),可是它的插入和刪除某個元素的效率卻是O(n)。因而導致構建有序數組變成低效的事情。
解決這些缺陷問題更好的方法應該是使用二叉查找樹了,最好自然是自平衡二叉查找樹了,既能高效的(O(n log n))構建有序元素集合,又能如同二分查找法一樣快速(O(log n))的搜尋目標數。
參考資料: