005-二分搜尋-分治法-《演算法設計技巧與分析》M.H.A學習筆記
阿新 • • 發佈:2019-01-25
二分搜尋又稱折半查詢,用於在排序好的序列表中進行搜尋,搜尋效率高,可在最壞的情況下用O(log n)完成搜尋任務。
基本思想:
將n個元素分成個數大致相同的兩半,取a[n/2]與欲查詢的x作比較,如果x=a[n/2]則找到x,演算法終止。如 果x<a[n/2],則我們只要在陣列a的左半部繼續搜尋x(這裡假設陣列元素呈升序排列)。如果x>a[n/2],則我們只要在陣列a的右 半部繼續搜尋x。
虛擬碼:
C++程式碼:
- int binSearch(constint *Array,int start,int end,int key)
- {
-
int left,right;
- int mid;
- left=start;
- right=end;
- while(left<=right)
- {
- mid=(left+right)/2;
- if(key==Array[mid]) return mid;
- elseif(key<Array[mid]) right=mid-1;
- elseif(key>Array[mid]) left=mid+1;
- }
- return -1;
- //找不到就返回-1
- }
“最小化最大值”:
這一演算法運用在stl的兩個函式中:
lower_bound
給定長度為n的單調不下降數列a0,a1,…,an−1和一個數k,求滿足ai≥k條件的最小的i。不存在的情況下輸出 n。
upper_bound
給定長度為n的單調不下降數列a0,a1,…,an−1和一個數k,求滿足ai >k條件的最小的i。不存在的情況下輸出 n。
C++程式碼:
兩種寫法(區別在判斷截止的條件,本質上並沒有分別):
1.
- while( rb > lb )
- {
- int m = (lb + rb) / 2;
- if( ok(m) ) rb = m;
-
else lb = m + 1;
- }
- // 答案為: lb == rb
2.
- while( rb - lb > 1 )
- {
- int m = (lb + rb) / 2;
- if( ok(m) ) rb = m;
- else lb = m;
- }
- // 跳出迴圈時 lb + 1 == rb
- // 答案為 rb<span style="font-family: Arial, Helvetica, sans-serif; background-color: rgb(255, 255, 255);"> </span>
貼兩個常用的程式碼:
- // 在arr的[sl, sr)下標範圍內查詢左起第一個>=target的元素,找到返
- // 回所在下標, 否則返回sr(區間最右元素下標+1)
- int LowerBound(int *arr, int sl, int sr, int target) {
- int l = sl, r = sr - 1, mid;
- while (l <= r) {
- mid = (l +r) >>1;
- if (arr[mid] < target) l = mid + 1;
- else r = mid;
- }
- return (l == r ? r : sr);
- }
- // 在arr的[sl, sr)下標範圍內查詢左起第一個>target的元素,找到返
- // 回所在下標, 否則返回sr(區間最右元素下標+1)
- int UpperBound(int *arr, int sl, int sr, int target) {
- int l = sl, r = sr - 1, mid;
- while (l <= r) {
- mid = (l +r) >>1;
- if (arr[mid] <= target) l = mid + 1;
- else r = mid;
- }
- return (l == r ? r : sr);
- }
浮點數的二分搜尋
關鍵在於截止條件,我們需要比較的是兩個數的接近程度而不是大小abs(l-r)>=1e-10,當然我們可以直接指定迴圈次數,如100次,可以達到2−100≈10−30 的精度範圍。
二分搜尋的演算法分析:
書中這一段寫得很清楚,就直接截圖了:
這裡的j就是搜尋樹的高度,即查詢的複雜度為O(log n)。