二分查找法

(一)前言

剛學完二分查找法,雖然該方法很簡單,可是仍是頗有不少坑值得記錄一下。java

(二)方法介紹

二分查找法的時間複雜度爲對數階,因此他相對於順序查找法來講效率較高。git

(三)代碼分析

這裏我決定分塊闡述,不直接把代碼端上來了。應爲咱們知道,這裏可能會有兩種狀況,一種是咱們只查詢單個數的索引,它沒有重複的數據;另外一種則是數組中有重複數據的出現。算法

(1)單個數據,返回值爲int類型的索引

1)main方法準備

這裏咱們使用了基數排序法(也是對昨天所學的一個複習)對數組排了序,因此最後返回的是排好序的索引結果。數組

public static void main(String[] args) {
        int[] arr = { 1, 2, 5, 4, 3, 15, 22 };
        BucketSort(arr);
        System.out.println(Arrays.toString(arr));
        ArrayList<Integer> result = binarySearch02(arr, 0, 8, 3);
        System.out.println(result);
    }

2)基數排序法

此方法能夠見我上一篇文章的詳解,這裏再也不贅述。函數

/**
 * 基數排序法
 * 
 * @param arr
 */
private static void BucketSort(int[] arr) {
    int[][] bucket = new int[10][arr.length];
    int[] bucketCounts = new int[10];

    int max = arr[0];
    for (int i = 1; i < arr.length; ++i) {
        if (arr[i] > max) {
            max = arr[i];
        }
    }
    int maxLength = (max + "").length();

    for (int i = 0, n = 1; i < maxLength; ++i, n *= 10) {
        for (int j = 0; j < arr.length; ++j) {
            int digitofElement = arr[j] / n % 10;
            bucket[digitofElement][bucketCounts[digitofElement]] = arr[j];
            bucketCounts[digitofElement]++;
        }
        int index = 0;
        for (int k = 0; k < bucket.length; ++k) {
            if (bucketCounts[k] != 0) {
                for (int l = 0; l < bucketCounts[k]; ++l) {
                    arr[index] = bucket[k][l];
                    index++;
                }
            }
            bucketCounts[k] = 0;
        }

    }
}

3)二分查找核心算法

這裏採用了遞歸而沒有使用循環,一方面是對本身遞歸的一個鍛鍊,另外一個方面遞歸的效率優於循環。code

private static int binarySearch(int[] arr, int left, int right, int value) {
    if (left > right) {
        return -1;
    }
    int mid = (left + right) / 2;
    if (value > arr[mid]) {
        return binarySearch(arr, mid + 1, right, value);
    } else if (value < arr[mid]) {
        return binarySearch(arr, left, mid - 1, value);
    } else {
        return mid;
    }
}
  • 首先,咱們須要給方法傳參(arr數組,left爲左索引,right爲右索引,value爲索引值),記住:遞歸的一個十分十分關鍵的一點,就是退出條件,若是沒有退出條件,他會進入死循環,出現StackOverflowError錯誤。
  • 這裏咱們判斷遞歸出去的條件是要麼找到此數,要麼沒找到。若是沒找到,那麼最後跳出條件必定是 left > right。但若是找到那麼最後跳出的就是mid參數。
  • 若是再也不跳出之列,那麼最後進入二級判斷:是往左找仍是往右找的問題,因此有了這幾個if-else判斷。

注意:這裏也是我在敲代碼的時候領悟的一點:使用遞歸時,若是函數有返回值,那麼遞歸遞歸調用也必須 return!!!這有這樣,在最底層return回來的數據才能被return給調用者。排序

(2)多個數據返回,返回類型爲集合

這裏的1)、2)跟前面相差不大,主要是對查找算法的改變。遞歸

3)二分查找核心算法

private static ArrayList<Integer> binarySearch02(int[] arr, int left, int right, int value) {
    if (left > right) {
        return new ArrayList<Integer>();
    }
    int mid = (left + right) / 2;
    if (value > arr[mid]) {
        return binarySearch02(arr, mid + 1, right, value);
    } else if (value < arr[mid]) {
        return binarySearch02(arr, left, mid - 1, value);
    } else {
        ArrayList<Integer> list = new ArrayList<>();
        int tmp = mid - 1;
        while (true) {
            if (tmp < 0 || arr[tmp] != value) {
                break;
            }
            list.add(tmp);
            tmp--;
        }
        list.add(mid);
        tmp = mid + 1;
        while (true) {
            if (tmp > arr.length || arr[tmp] != value) {
                break;
            }
            list.add(tmp);
            tmp++;
        }
        return list;
    }
}

改變之處:索引

  1. 返回值:如今返回一個ArrayList集合,裏面放索引
  2. 遞歸結束返回值:空集合
  3. 最後返回mid位置的數的改進:由於若是找到,那麼可能該數左右都有,因此這裏用兩個while循環將其收入。最後遞歸返回(本文關鍵,提升對遞歸的理解)。
相關文章
相關標籤/搜索