Java實現二分查詢演算法

morethink發表於2018-06-05

二分查詢(binary search),也稱折半搜尋,是一種在 有序陣列查詢某一特定元素 的搜尋演算法。搜尋過程從陣列的中間元素開始,如果中間元素正好是要查詢的元素,則搜尋過程結束;如果某一特定元素大於或者小於中間元素,則在陣列大於或小於中間元素的那一半中查詢,而且跟開始一樣從中間元素開始比較。如果在某一步驟陣列為空,則代表找不到。這種搜尋演算法每一次比較都使搜尋範圍縮小一半。

  • 時間複雜度:折半搜尋每次把搜尋區域減少一半,時間複雜度為O(log n)。(n代表集合中元素的個數)
  • 空間複雜度: O(1)。雖以遞迴形式定義,但是尾遞迴,可改寫為迴圈。

動圖演示

binary-search
二分查詢

程式碼描述

遞迴

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))的搜尋目標數。

參考資料

  1. 二分查詢法的實現和應用匯總
  2. 二分查詢(Binary Search)需要注意的問題,以及在資料庫核心中的實現

相關文章