二分搜尋之搜尋陣列中目標元素的首尾下標

DoubleFJ發表於2018-10-15

個人部落格:DoubleFJ の Blog

今天總結一下二分搜尋。假設這裡的陣列已經是升序排序好了的。

我們知道二分搜尋的效率很高,它充分利用了元素間的次序關係,採用分治策略,可在最壞的情況下用 O(log n) 完成搜尋任務。它的基本思想:將 n 個元素分成個數大致相同的兩半,取 a[n/2] 與需要查詢的目標值 x 作比較,如果 x=a[n/2] 則找到 x,演算法運算終止。詳情可跳轉百度百科

  • 我們通常最基本的二分搜尋是這樣實現的(Java):
    /**
     * 二分搜尋
     * 
     * @param arr
     *            已升序排序陣列
     * @param key
     *            目標查詢值
     * @return
     */
    public static int commonBinarySearch(int[] arr, int key) {
        int low = 0;
        int high = arr.length - 1;
        int middle = 0; // 定義middle

        if (key < arr[low] || key > arr[high] || low > high) {
            return -1;
        }

        while (low <= high) {
            middle = (low + high) / 2;
            if (arr[middle] > key) {
                // 比關鍵字大則關鍵字在左區域
                high = middle - 1;
            } else if (arr[middle] < key) {
                // 比關鍵字小則關鍵字在右區域
                low = middle + 1;
            } else {
                return middle;
            }
        }

        return -1; // 未找到結果,返回-1
    }

若是陣列中存在搜尋目標元素,則只要查詢到任意一個便會返回該值;若是沒有找到即返回 -1。

  • 來看下一種,只返回目標元素第一次出現的位置下標(虛擬碼):
l = -1; u = n
while l+1 != u
    m = (l + u) / 2
    if x[m] < t
        l = m
    else
        u = m
p = u
if p >= n || x[p] != t
    p = -1

n 為陣列的長度,p 就是最終我們需要的下標。若是 while 迴圈出來的最終結果 u >= n (其實最大也只會等於 n)或者 x[u] != t(t 為我們的目標元素),那麼也就是無結果,返回 -1。

  • 我們再來看最後一個 function(該方法引數不同使得結果可以返回元素第一個出現的下標或者最後一個下標,也是 Java):
    /**
     * 
     * @param nums
     *            已經升序排序好的陣列
     * @param target
     *            搜尋目標元素
     * @param left
     *            是否是查詢第一個元素下標。true:查詢目標元素第一個出現下標, false:查詢目標元素最後一個出現下標
     * @return
     */
    private int extremeInsertionIndex(int[] nums, int target, boolean left) {
        int lo = 0;
        int hi = nums.length;

        while (lo < hi) {
            int mid = (lo + hi) / 2;
            if (nums[mid] > target || (left && target == nums[mid])) {
                hi = mid;
            } else {
                lo = mid + 1;
            }
        }
        return lo;
    }

目標元素出現的第一個下標:int leftIdx = extremeInsertionIndex(nums, target, true);

目標元素出現的最後一個下標:int rightIdx = extremeInsertionIndex(nums, target, false) - 1;這裡需要減一,需要注意。


總結一下:二分搜尋只需要注意它的邊界值,原先的陣列下標範圍是 [0…n-1],當你用 lo = 0, hi = n-1 去執行時,對應條件滿足情況下值的賦值應該是 lo = mid + 1, hi = mid - 1;而若是用 lo = -1, hi = n 去作為條件執行時,對應條件滿足情況下值的賦值就應該為 lo = mid, hi = mid,因為它兩個值都是作為邊界值。[0, n]、[-1, n-1]也是如此。


相關連結:

相關文章