5、二分法查找

1、二分法查找介紹

  • 二分查找針對的是一個有序的數據集合,查找思想有點相似分治思想。
  • 每次都經過跟區間的中間元素對比,將待查找的區間縮小爲以前的一半,直到找到要查找的元素,或者區間被縮小爲0。
二分法查找

1.一、二分法查找的時間複雜度

  • 咱們假設數據大小是 n,每次查找後數據都會縮小爲原來的一半,也就是會除以2。最壞狀況下,直到查找區間被縮小爲空,才中止。java

  • 這是一個等比數列。其中 n/2^k = 1 時, k 的值就是總共縮小的次數。算法

  • 而每一次縮小操做只涉及兩個數據的大小比較,因此,通過了 k 次區間縮小操做,時間複雜度就是 O(k)。經過 n/2^k = 1,咱們能夠求得 k= log2n,因此時間複雜度就是 O(logn)數組

  • O(logn) 這種對數時間複雜度。這是一種極其高效的時間複雜度,有的時候甚至比時間複雜度是常量級 O(1) 的算法還要高效。數據結構

  • 由於 logn 是一個很是「恐怖」的數量級,即使 n 很是很是大,對應的 logn 也很小。code

  • 好比 n 等於2的32次方,這個數大約是42億。也就是說,若是咱們在 42 億個數據中用二分查找一個數據,最多須要比較32次。blog

  • 大 O 標記法表示時間複雜度的時候,會省略掉常數、係數和低階。排序

  • 對於常量級時間複雜度的算法來講, O(1) 有可能表示的是一個很是大的常量值,好比 O(1000)、 O(10000)。遞歸

  • 因此,常量級時間複雜度的算法有時候可能尚未 O(logn) 的算法執行效率高。內存

  • 反過來,對數對應的就是指數。指數時間複雜度的算法在大規模數據面前是無效的。class

1.二、二分法的實現

一、非遞歸實現

/**
 * 二分法查找,非遞歸實現
 *
 * @param arr
 * @param a
 * @return
 */
public static int binarySearch(int[] arr, int a) {
    int start = 0;
    int end = arr.length - 1;

    while (start <= end) { // 不能是start < end
        int mid = start + ((end - start) >> 1); //start + (end - start)/2 位運算符速度快
        if (arr[mid] == a) {
            return mid;
        } else if (arr[mid] > a) {
            end = mid - 1; // end = min 有時會形成死循環
        } else {
            start = mid + 1;
        }
    }
    return -1;
}

二、遞歸實現

/**
 * 二分法遞歸實現
 *
 * @param arr
 * @param start
 * @param end
 * @param a
 * @return
 */
public static int binarySearch2(int[] arr, int start, int end, int a) {
    if (start > end) return -1;
    int mid = start + ((end - start) >> 1);
    if (arr[mid] == a) {
        return mid;
    } else if (arr[mid] > a) {
        end = mid - 1;
        return binarySearch2(arr, start, end, a);
    } else {
        start = mid + 1;
        return binarySearch2(arr, start, end, a);
    }
}

2、二分法查找的侷限性

二分查找的時間複雜度是O(logn),查找數據的效率很是高。不過,並非什麼狀況下均可以用二分查找,它的應用場景是有很大侷限性的。

  • 首先,二分查找依賴的是順序表結構,就是數組

    • 二分查找不能依賴鏈表。緣由二分查找算法須要按照下標隨機訪問元素。
    • 數組按照下標隨機訪問數據的時間複雜度是 O(1),而鏈表隨機訪問的時間複雜度是 O(n)。
    • 因此,若是數據使用鏈表存儲,二分查找的時間複雜就會變得很高。
    • 二分查找只能用在數據是經過順序表來存儲的數據結構上。若是你的數據是經過其餘數據結構存儲的,則沒法應用二分查找。
  • 二分查找針對的是有序數據

    • 二分查找對這一點的要求比較苛刻,數據必須是有序的。若是數據沒有序,咱們須要先排序。
    • 序的時間複雜度最低是 O(nlogn)。因此,若是針對的是一組靜態的數據,沒有頻繁地插入、刪除,咱們能夠進行一次排序,屢次二分查找。
    • 這樣排序的成本可被均攤,二分查找的邊際成本就會比較低。
    • 若是數據集合有頻繁的插入和刪除操做,要想用二分查找,要麼每次插入、刪除操做以後保證數據仍然有序,要麼在每次二分查找以前都先進行排序。
    • 針對這種動態數據集合,不管哪一種方法,維護有序的成本都是很高的。
    • 因此,二分查找只能用在插入、刪除操做不頻繁,一次排序屢次查找的場景中。針對動態變化的數據集合,二分查找將再也不適用。
  • 數據量太大也不適合二分查找。

    • 二分查找的底層須要依賴數組這種數據結構,而數組爲了支持隨機訪問的特性,要求內存空間連續,對內存的要求比較苛刻。
    • 好比,有 1GB 大小的數據,若是但願用數組來存儲,那就須要 1GB 的連續內存空間。
    • 注意這裏的「連續」二字,也就是說,即使有 2GB 的內存空間剩餘,可是若是這剩餘的 2GB 內存空間都是零散的,沒有連續的 1GB 大小的內存空間,那照樣沒法申請一個1GB大小的數組。
    • 而二分查找是做用在數組這種數據結構之上的,因此太大的數據用數組存儲就比較吃力了,也就不能用二分查找了。
相關文章
相關標籤/搜索