數據結構和算法之——二分查找上
二分查找(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」!
數據結構和算法之——二分查找上