c++實現二分查詢
簡要描述
二分查詢又稱折半查詢,對排好序的陣列,每次取這個數和陣列中間的數進行比較,複雜度是O(logn)如:設陣列為a[n],查詢的數x,
如果x==a[n/2],則返回n/2;
如果x < a[n/2],則在a[0]到a[n/2-1]中進行查詢;
如果x > a[n/2],則在a[n/2+1]到a[n-1]中進行查詢;
優點是比較次數少,查詢速度快,平均效能好;其缺點是要求待查表為有序表,且插入刪除困難。
條件:查詢的陣列必須要為有序陣列。
二種實現方式
1.遞迴
/* 歸的二分查詢 arrat:陣列 , low:上界; high:下界; target:查詢的資料; 返回target所在陣列的下標 */ int binarySearch(int array[], int low, int high, int target) { int middle = (low + high)/2; if(low > high) { return -1; } if(target == array[middle]) { return middle; } if(target < array[middle]) { return binarySearch(array, low, middle-1, target); } if(target > array[middle]) { return binarySearch(array, middle+1, high, target); } }
2.非遞迴(迴圈)
/* 非遞迴的二分查詢 arrat:陣列 , n:陣列的大小; target:查詢的資料; 返回target所在陣列的下標 */ int binarySearch2(int array[], int n, int target) { int low = 0, high = n, middle = 0; while(low < high) { middle = (low + high)/2; if(target == array[middle]) { return middle; } else if(target < array[middle]) { high = middle; } else if(target > array[middle]) { low = middle + 1; } } return -1; }
推薦使用非遞迴的方式,因為遞迴每次呼叫遞迴時有用堆疊儲存函式資料和結果。能用迴圈的儘量不用遞迴。
二分查詢的應用
還是對上一篇博文《C++如何跳出多層迴圈》中提到的抽籤問題進行分析。
上一篇博文中是進行了四重迴圈的巢狀,基時間複雜度是O(n4),資料大時其計算量會大的驚人。為便於分析,將之前程式碼帖至如下:
|
最內層迴圈所做事如下:
Ka + kb + kc + kd = m
移項後如下:
Kd = m - (Ka + kb + kc)
到第四層迴圈時,其實Ka ,kb,kc已經知道,那問題也就變成了對kd的查詢,我們可用上面講的二分查詢,複雜度就降為O(n3logn).實現如下:
降低複雜度的實現
|
進一步優化[O(n2logn)]
根據上一步的優化方式,我們可以進一步對內側兩層迴圈(也就是第三層和第四層)進行思考:
Kc+ kd = m - (Ka + kb)
我們不能直接對Kc+ kd進行查詢,但是可以預先枚舉出Ka + kb 的n2種數值並排序,再對Kc+ kd進行十分查詢。列出列舉O(n2),排序O(n2logn), 迴圈O(n2logn),所以總的複雜度降為O(n2logn),實現如下:
|
進一步思考
上面列舉Ka + kb 時其實是有重複的,因為k[i] + k[j] == k[j] + k[i],去除重複值之後,Ka + kb 值的個數是n(n+1)/2。至於n(n+1)/2怎麼來,可以簡單推導如下:
N M
1 1
2 2+1
3 3+2+1
4 4+ 3+2+1
......
實現如下:
|