歸併排序中對小陣列採用插入排序
儘管合併排序最壞情況執行時間為O(nlgn),插入排序的最壞執行時間為O(n^2),但是插入排序的常數因子使得它在n較小時,執行要更快一些。因此,在合併排序演算法中,當子問題足夠小時,採用插入排序就比較合適了。考慮對合並排序做這樣的修改,即採用插入排序策略,對n/k 個長度為 k 的子列表進行排序。然後,再用標準的合併機制將它們合併起來,此處k是一個待定的值。
注:書裡的那個O裡還有一槓的符號打不出來,所以這裡用大O代替了,特指同階無窮大量。
a) 證明在最壞的情況下,n/k個子列表可以用插入排序在O(nk)的時間內完成排序
b) 證明這些子列表可以在O(nlg(n/k))最壞情況內完成合並。
c) 如果已知修改後的合併排序演算法的最壞執行時間為O(nk+nlg(n/k)),要使得修改後的演算法具有與標準合併演算法一樣的漸進執行時間,k的最大漸進值( 即O形式)是什麼(以n的函式形式表示)?
d) 實踐中,應該如何選取k值。
最佳答案
第一個O(n/k*k^2)=O(nk).這裡的O中間有一橫
第二個n/k個列表兩兩合併,合併完繼續合併,共lg(n/k)對,合併的代價O(n).所以O(nlg(n/k)).
如果僅僅由兩個字表合併,則每個資料被查詢且移動一次,而n/k個字表時,每個陣列查詢且移動lg(n/k)次,
故為O(nlg(n/k)).
第三個O(nk+nlg(n/k))=O(nlgn).只能最大是k=O(lgn).等式左邊中第一項是高階項。k如果大於lgn,則比歸併排序複雜度大了。左邊可以寫成nk+nlgn-nlgk,k等於lgn時,就是2nlgn-nlglgn.與歸併排序是一樣的。
第四個k應當是最大的列表長度,這個長度上插入排序要比歸併排序快。
#include<iostream> #include<ctime> #include <limits> #include <cmath> #include <boost/timer/timer.hpp> using namespace boost::timer; using namespace std; void merge(int *a,int p,int q,int r)//歸併哨兵法 { int Ln=q-p+1; int Rn=r-q; int *La=new int[Ln+1]; int *Ra=new int[Rn+1]; for(int i=0;i<Ln;++i){ La[i]=a[i+p]; } La[Ln]=numeric_limits<int>::max(); for(int j=0;j<Rn;++j){ Ra[j]=a[q+j+1]; } Ra[Rn]=numeric_limits<int>::max(); int k=0,m=0; for(int n=p;n<=r;++n){ if(La[k]<Ra[m]){ a[n]=La[k]; ++k; }else{ a[n]=Ra[m]; m++; } } delete [] La; delete [] Ra; } void insert_sort(int *a ,int n)//直接插入法 { int key,j; for(int i=1;i<n;++i){ key=a[i]; j=i-1; while(j>=0 && key<a[j]){ a[j+1]=a[j]; --j; } a[j+1]=key; } } void merge_insertion_sort(int *a,int p,int q,int k)//歸併,直接插入聯合使用 { if(p<q){ if((q-p+1)>k){ int mid=(p+q)>>1; merge_insertion_sort(a,p,mid,k); merge_insertion_sort(a,mid+1,q,k); merge(a,p,mid,q); }else{ insert_sort(a+p,q-p+1); } } } void merge_sort(int *a,int p,int q) { if(p<q){ int mid=(p+q)>>1; merge_sort(a,p,mid); merge_sort(a,mid+1,q); //merge(a,p,mid,q); merge(a,p,mid,q); } } int main() { srand(time(NULL)); int n; //while((n= rand()%100)<3); n=500000; int * a=new int[n]; int * b=new int[n]; for(int i=0;i<n;i++){ a[i]=rand()%100000; b[i]=a[i]; } cpu_timer t; merge_insertion_sort(a,0,n-1,log(n)/log(2)); t.stop(); cpu_timer t2; merge_sort(b,0,n-1); t2.stop(); delete [] a; delete [] b; cout<<t.format(); cout<<t2.format(); }