二分查詢演算法詳解
最近刷了很多二分查詢相關的題目,這裡將近期的收穫做一個總結,包括二分查詢的變形問題。如果能掌握,我相信以後基本上二分查詢相關的問題對你來說,都不是問題。
2
|
0
二分查詢的效率
二分查詢是啥我想不用過多的說明。我們都知道二分查詢的時間複雜程度是O(logN)。
O(logn) 查詢速度有多快呢?我們來分析一下。
我們假設資料大小是 n,每次查詢後資料都會縮小為原來的一半,也就是會除以 2。最壞情況下,直到查詢區間被縮小為空,才停止。
就因為這種特性,有的時候甚至比時間複雜度是常量級 O(1) 的演算法還要高效。為什麼這麼說呢?
因為 logn 是一個非常“恐怖”的數量級,即便 n 非常非常大,對應的 logn 也很小。比如 n 等於 2 的 32 次方,這個數很大了吧?大約是 42 億。也就是說,如果我們在 42 億個資料中用二分查詢一個資料,最多需要比較 32 次。
3
|
0
簡單的二分查詢
簡單的二分查詢我想大家應該都寫過。但是想一次將二分查詢寫對估計10個人裡面只有1個人能做到。下面給出題目和程式碼,我們具體來分析一下。
題目:在有序的陣列a裡,找到某個指定的資料value。
public int bsearch(int[] a, int value) { int low = 0; int high = a.length - 1; while (low <= high) { int mid = (low + high) / 2; if (a[mid] == value) { return mid; } else if (a[mid] < value) { low = mid + 1; } else { high = mid - 1; } } return -1; }
上訴程式碼可以作為一個二分查詢的模板程式碼,我相信你能輕易的看懂這段程式碼。這裡需要強調幾個容易出錯的地方:
1.迴圈退出條件:
注意是low<=high,而不是 low
2.mid 的取值:
實際上,mid=(low+high)/2 這種寫法是有問題的。因為如果 low 和 high 比較大的話,兩者之和就有可能會溢位。改進的方法是將 mid 的計算方式寫成low+(high-low)/2。更進一步,如果要將效能最佳化到極致的話,我們可以將這裡的除以 2 操作轉化成位運算 low+((high-low)>>1)。因為相比除法運算來說,計算機處理位運算要快得多。
3.low 和 high 的更新
low=mid+1,high=mid-1。注意這裡的 +1 和 -1,如果直接寫成 low=mid 或者 high=mid,就可能會發生死迴圈。比如,當 high=3,low=3 時,如果 a[3]不等於 value,就會導致一直迴圈不退出。
4
|
0
二分查詢的變形
上面這種簡單的二分查詢大家基本上都會寫,需要注意的就是幾個容易出錯的地方。爭取這種簡單的二分查詢都是一次性透過。
我們平常很少會寫這種簡單的二分查詢,這裡我給出幾種常見的變形的二分查詢演算法。我們來觀察其通用性,掌握其技巧。
1. 題目:查詢第一個值等於給定值的元素 例子:a:[1,3,4,5,6,,6,6,7,8],value:6。那麼需要定位到下標為4的元素
public int bsearch(int[] a, int value) { int low = 0; int high = a.length - 1 - 1; while (low <= high) { int mid = low + ((high - low) >> 1); if (a[mid] > value) { high = mid - 1; } else if (a[mid] < value) { low = mid + 1; } else { if ((mid == 0) || (a[mid - 1] != value)) return mid; else high = mid - 1; } } return -1; }
仔細看看上訴演算法中,與之前的簡單的二分查區別在哪裡?
我們來分析一下。首先我們還是按照簡單的二分查詢來算。當找到指定值的時候我們不能直接放回結果。需要再判斷一下,左邊的元素與自身是否相同。不相同則返回結果。否則繼續二分。
2. 題目:查詢第一個大於等於給定值的元素 例子:a:[3,4,6,7,10] 如果查詢第一個大於等於5的元素,那就是6
public int bsearch(int[] a, int value) { int low = 0; int high = a.length - 1 - 1; while (low <= high) { int mid = low + ((high - low) >> 1); if (a[mid] >= value) { if ((mid == 0) || (a[mid - 1] < value)) return mid; else high = mid - 1; } else { low = mid + 1; } } return -1; }
如果 a[mid]小於要查詢的值 value,那要查詢的值肯定在[mid+1, high]之間,所以,我們更新 low=mid+1。
對於 a[mid]大於等於給定值 value 的情況,我們要先看下這個 a[mid]是不是我們要找的第一個值大於等於給定值的元素。
如果 a[mid]前面已經沒有元素,或者前面一個元素小於要查詢的值 value,那 a[mid]就是我們要找的元素。這段邏輯對應的程式碼是第 7 行。
如果 a[mid-1]也大於等於要查詢的值 value,那說明要查詢的元素在[low, mid-1]之間,所以,我們將 high 更新為 mid-1。
5
|
0
總結
要想寫好二分查詢,首先我們必須保證充分理解最簡單的二分查詢演算法。不熟悉的話多寫幾遍。
當遇到變形的二分查詢。我們需要改動簡單的二分查詢程式碼。改動的點就在a[mid]與value的對比上加上相應的條件。
上面的兩道變形的演算法如果你多做幾遍一定會有自己的體會。
最後,需要特別主義的還是那三個特別容易出錯的地方:
1.迴圈退出條件:
2.mid 的取值:
3.low 和 high 的更新
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/69984138/viewspace-2732789/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- 詳解二分查詢
- 如何找東西?查詢演算法之順序查詢和二分查詢詳解演算法
- 查詢演算法__二分查詢演算法
- 查詢演算法之二分查詢演算法
- 二分查詢演算法演算法
- 演算法->二分查詢演算法
- 【演算法】二分查詢演算法
- 二分查詢【折半查詢】演算法 PHP 版演算法PHP
- 資料結構和演算法面試題系列—二分查詢演算法詳解資料結構演算法面試題
- 查詢——二分查詢
- 演算法(一):二分查詢法演算法
- 重溫二分查詢演算法演算法
- [演算法]各種二分查詢演算法
- c#-二分查詢-演算法C#演算法
- 圖解--二分查詢樹圖解
- 【演算法】二分查詢與暴力查詢(白名單過濾)演算法
- 小白懂演算法之二分查詢演算法
- 二分查詢及其變種演算法演算法
- 演算法一 二分查詢( by Python)演算法Python
- Java實現二分查詢演算法Java演算法
- LeetCode演算法—二分查詢LeetCode演算法
- 二分查詢演算法詳講(三種版本寫法)原創演算法
- 二分法查詢介紹及例項詳解
- 二分查詢(一)——純粹的二分查詢
- Python查詢-二分查詢Python
- 二分查詢
- 每日一道演算法:二分查詢演算法
- 查詢演算法之二分法演算法
- 死磕演算法之二分查詢法演算法
- 順序查詢和二分查詢
- 減治思想——二分查詢詳細總結
- PHP二分查詢PHP
- 二分查詢法
- 從酒桌遊戲看二分查詢演算法遊戲演算法
- 資料結構與演算法-二分查詢資料結構演算法
- 【資料結構與演算法】—— 二分查詢資料結構演算法
- 前端開發中的二分查詢演算法前端演算法
- 資料結構與演算法——二分查詢演算法資料結構演算法