1. 程式人生 > >數據結構和算法之——二分查找上

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

clas 算法實現 字符 我會 orien 如果 元素 時間復雜度 urn

二分查找(Binary Search)的思想非常簡單,但看似越簡單的東西往往越難掌握好,想要靈活運用就更加困難。

1. 二分查找的思想?

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

技術分享圖片

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

技術分享圖片

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

2. 二分查找的時間復雜度?

我們假設數據大小為 n,每次查找數據大小都會縮小為原來的一半,最壞情況下,直到查找區間縮小為空時停止查找。

技術分享圖片

若經過 k 次區間縮小最後變為空,則 $\frac{n}{2^k} = 1, k = log_2n$,所以二分查找的時間復雜度為 $O(logn)$。

這種對數時間復雜度的算法是一種非常高效的算法,有時候甚至比時間復雜度為常量級的算法還要高效。因為常量級的時間復雜度對應的常數可能非常大,比如 O(100), O(1000),因此這些算法有時候可能還沒有 $O(logn)$ 的算法執行效率高。

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

循環法

float Binary_Search(float data[], int left, int right, float value)
{
    while (left <= right)
    {
        int mid = left + (right - left) / 2;
        if (value == data[mid])
        {
            return mid;
        }
        else if (value < data[mid])
        {
            right = mid - 1;
        }
        else
        {
            left = mid + 1;
        }
    }

    return -1;
}

遞歸法

float Binary_Search(float data[], int left, int right, float value)
{
    if (left <= right)
    {
        int mid = left + (right - left) / 2;
        if (value == data[mid])
        {
            return mid;
        }
        else if (value < data[mid])
        {
           return Binary_Search(data, left, mid-1, value);
        }
        else
        {
            return Binary_Search(data, mid+1, right, value);
        }
    }

    return -1;
}

註意事項

  • 循環退出條件 left <= right
  • mid = left + ((right-left) >> 1),用移位運算優化計算性能
  • left 和 right 的更新分別是 mid+1 和 mid-1

3. 二分查找的應用場景?

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

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

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

而如果數據量過大,則意味著我們需要用非常大的連續內存空間來存儲這些數據,內存開銷可能無法滿足

獲取更多精彩,請關註「seniusen」!
技術分享圖片

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