查找算法之二分查找法

查找算法之二分查找法

思想

二分查找法的思想很是簡單,對於一個有序數列,找它中間的元素,看是不是查找目標,若是不是,就看這個查找目標是小於仍是大於中間元素,而後在對應的區間內重複上述過程。java

算法

須要注意幾個問題:git

  • while 循環:while 循環的條件應該是 left < right 仍是 left <= right 呢?github

    • 這能夠從咱們設置的左右邊界判斷出來,咱們設置的 left = 0, right = n - 1,所以 [left, right] 是一個閉區間,那麼當 left = right 時,[left, right] 區間一樣知足咱們的設置,所以,這個循環內應該是 left <= right。
  • target 和 arr[mid] 的判斷:當 target > arr[mid] 時,是應該 left = mid 仍是 left = mid + 1 呢?算法

    • 這和在 while 中的判斷是一個思路,當 target > arr[mid] 時,target 在 [mid + 1, r] 中,而非 [mid, r],由於顯然此時 mid 已經不可能等於 target 了,所以咱們不須要再比較 target 和 arr[mid]。
    • 一樣地,當 target < arr[mid] 時,也是相似的判斷。
  • 循環不變量:left 和 right 的定義十分重要,由於在後面咱們要不斷地維護這個定義,咱們必需要保證是在 [left, right] 這個區間裏尋找 target,這也就是 循環不變量,意思就是 left 和 right 的值雖然一直在變化,可是有一個聲明 在 [left, right] 區間內尋找 target 是永遠不變的,只要咱們維護住這個 循環不變量,那麼就能夠保證咱們的算法是正確的。固然,這裏你也能夠定義成 left = 0, right = n,即[left, right),那麼此時定義就發生了變化,相應地,在 while 中爲了維護這個定義,咱們須要把條件改爲 left < right,由於當 left = right 時,顯然 [left, right)是錯誤的,後面再查找時也要作相應的修改。
private int binarySearch(T arr[], int n, T target) {

        int left = 0, right = n - 1; // 在 [left, right] 區間內尋找 target
        
        while (left <= right) { // 當 left = right 時,區間 [left, right] 仍然有效
            int mid = (left + right) / 2;
            if (arr[mid] == target)
                return mid;
            if (target > arr[mid])
                left = mid + 1; // target 在 [mid+1, r] 中
            else
                right = mid - 1; // target 在 [left, mid - 1] 中
        }

        return -1;
    }

不知道到這裏你們有沒有發現一個 bug 測試

由於 left 和 right 都是 int,因此當值足夠大時,在計算 mid = (left + right) / 2 時可能會發生整型溢出!code

所以,爲了不這個問題,咱們使用減法來計算。get

徹底正確的二分查找法it

public int binarySearch(T[] arr, int n, T target) {

        int left = 0, right = n - 1; // 在 [left, right] 區間內尋找 target

        while (left <= right) { // 當 left = right 時,區間 [left, right] 仍然有效
            int mid = left + (right - left) / 2;
            if (arr[mid].compareTo(target) == 0)
                return mid;
            if (target.compareTo(arr[mid]) > 0)
                left = mid + 1; // target 在 [mid+1, r] 中
            else
                right = mid - 1; // target 在 [left, mid - 1] 中
        }

        return -1;
    }

測試

咱們能夠寫一個 Util 來幫助咱們生成測試用例io

ArrayUtil.javaast

public static Integer[] generateSortedArray(int n) {

        assert n > 0;

        Integer[] arr = new Integer[n];
        for (int i = 0; i < n; i++) {
            arr[i] = i;
        }

        return arr;
    }

BinarySearch.java

public static void main(String[] args) {

        BinarySearch<Integer> bs = new BinarySearch<Integer>();
        int n = (int)Math.pow(10, 7); // 用 10,000,000 數據測試
        Integer[] data = ArrayUtil.generateSortedArray(n);

        long startTime = System.currentTimeMillis();
        for (int i = 0; i < n; i++)
            if (i != bs.binarySearch(data, n, i))
                throw new IllegalStateException("find i failed");
        long endTime = System.currentTimeMillis();

        System.out.println("Binary Search success!");
        System.out.println("Time cost: " + (endTime - startTime) + "ms");
    }

完整代碼:github: My-Notes/algorithm(Java)/01-binarySearch/src/

相關文章
相關標籤/搜索