數據結構和算法之二分查找

1.二分法查找定義

二分查找也稱折半查找(Binary Search),它是一種效率較高的查找方法。 它是對一組有序的數字中進行查找,傳遞相應的數據,進行比較查找到與原數據相同的數據,查找到了返回對應的數組下標,沒有找到返回-1;

優點: 比較次數少,查找速度快,平均性能好 ,佔用系統內存較少

缺點: 待查表爲有序表,插入刪除困難

適用: 不經常變動而查找頻繁的有序列表

2.二分查找的思想

生活中二分查找的思想無處不在。一個最常見的就是猜數遊戲,我隨機寫一個 0 到 99 的數,然後你來猜我寫的是什麼。猜的過程中,我會告訴你每次是猜大了還是猜小了,直到猜中爲止。假如我寫的數是 23,猜數過程如下所示。

次數 猜測範圍 中間數 對比大小
第1次 0-99 49 49>23
第2次 0-48 24 24>23
第3次 0-23 11 11<23
第4次 12-23 17 17<23
第5次 18-23 20 20<23
第6次 21-23 22 22<23
第7次 23

最多只需要 7 次就猜出來了,這個過程是很快的。同理,要查找某個數據是否在給定的數組中,我們同樣也可以利用這個思想。

二分查找針對的是一個有序的數據集合,查找思想有點類似於分治,每次都通過和中間元素進行比較,將待查找區間縮小爲之前的一半,直到找到要查找的元素或者區間縮小爲 0 爲止。

3.簡單二分查找的算法實現

  • kotlin
//第一種:
/**
 * @param array 有序數組
 * @param left 數組低地址下標
 * @param right 數組高地址下標
 * @param key  查找元素
 */
fun binarySearch(data: IntArray, left: Int, right: Int, key: Int): Int {
    var low = left
    var high = right

    while (low <= high) {
        val mid = (low + high) / 2
        when {
            key == data[mid] -> return mid
            key > data[mid] -> low = mid + 1
            else -> high = mid - 1
        }
    }
    return -1
}

//第二種:遞歸法:

/**
 * @param array 有序數組
 * @param left 數組低地址下標
 * @param right 數組高地址下標
 * @param key  查找元素
 */
fun binarySearch(data: IntArray, left: Int, right: Int, key: Int): Int {

    while (left <= right) {
        val mid = (left + right) / 2
        return  when {
            key == data[mid] ->  mid
            
            key > data[mid] ->  binarySearch(data, mid + 1, right, key)// left = mid + 1
            
            else ->  binarySearch(data, left, mid - 1, key) //right = mid - 1
        }
    }
    return -1
}

注意事項

  • 循環退出條件 left <= right

  • mid = left + ((right-left) >> 1),用移位運算優化計算性能

  • left 和 right 的更新分別是 mid+1 和 mid-1

  • Java版

public static void main(String[] args) {
        int srcArray[] = {3, 5, 11, 17, 21, 23, 28, 30, 32, 50, 64, 78, 81, 95, 101};
        System.out.println(binSearch(srcArray, 0, srcArray.length - 1, 111));
        System.out.println(binSearch(srcArray, 78));
    }


    /**
     * 二分查找普通實現。
     *
     * @param srcArray 有序數組
     * @param key      查找元素
     * @return 不存在返回-1
     */
    private static int binSearch(int srcArray[], int key) {
        int mid;
        int start = 0;
        int end = srcArray.length - 1;
        while (start <= end) {
            mid = (end - start) / 2 + start;
            if (key < srcArray[mid]) {
                end = mid - 1;
            } else if (key > srcArray[mid]) {
                start = mid + 1;
            } else {
                return mid;
            }
        }
        return -1;
    }

    /**
     * 二分查找遞歸實現。
     *
     * @param srcArray 有序數組
     * @param start    數組低地址下標
     * @param end      數組高地址下標
     * @param key      查找元素
     * @return 查找元素不存在返回-1
     */
    private static int binSearch(int srcArray[], int start, int end, int key) {
        int mid = (end - start) / 2 + start;
        if (srcArray[mid] == key) {
            return mid;
        }
        if (start >= end) {
            return -1;
        } else if (key > srcArray[mid]) {
            return binSearch(srcArray, mid + 1, end, key);
        } else if (key < srcArray[mid]) {
            return binSearch(srcArray, start, mid - 1, key);
        }
        return -1;
    }

4.二分查找的應用場景

二分查 找依賴的是順序表結構,也就是數組,需要能夠按照下標隨機訪問元素。

二分查找針對的是有序數據,如果數據無序,需要先進行排序。而如果有頻繁的插入、刪除操作,則每次查找前都需要再次排序,這時候,二分查找將不再適用。

數據量太小可以直接遍歷查找,沒有必要用二分查找。但如果數據之間的比較操作非常耗時,比如數據爲長度超過 300 的字符串,則不管數據量大小,都推薦使用二分查找。

數據量太大不適合用二分查找。二分查找底層依賴數組這種數據結構,而數組爲了支持隨機訪問的特性,要求內存空間連續,對內存的要求比較苛刻。比如,我們有1GB1GB大小的數據,如果希望使用數組來存儲,那就需要1GB1GB的連續內存空間來存儲。

參考資料:

1.程傑 .大話數據結構. 北京:清華大學出版社, 2011

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章