分治法下的歸並算法(merge sort)
分支模式的三個步驟:
分解:將原問題分解為若干個子問題,子問題為原問題規模較小的問題
解決:遞歸求解子問題,若足夠小,直接求解
合並:將子問題的解合並為原問題的解
歸並算法(merge sort)
分解:分解待排序的n個元素的序列成各具n/2個元素的子序列
解決:使用歸並算法解決兩個子序列的排序
合並:合並兩個已排序的子序列
偽代碼實現歸並的過程
MERGE(A,p,q,r) //A代表一個數組,pqr分別代表下標(p<=q<r)
1 n1 = q-p+1
2 n2 = r-q
3 let L[1,2…,n1+1] and R[1,…n2+1] be new arrays
4 for i = 1 to n1
5 L[i]=A[p+i-1]
6 for j=1 to n2
7 R[j]=A[q+j]
8 L[n1+1]=∞
9 R[n2+1]=∞
10 i=1
11 j=1
12 for k = p to r
13 if L[i] <= R[j]
14 A[k]=L[i]
15 i = i+1
16 else A[k] =R[j]
17 j=j+1
這里是歸並過程的偽代碼,歸並過程作為本算法的另一個核心,其代碼過程也充分體現了排序的過程
總程序
MERGE-SORT(A,p,r)
1 if p<r
2 q=(p+r)/2
3 MERGE-SORT(A,p,q)
4 MERGE-SORT(A,q+1,r)
5 MERGE(A,p,q,r)
這里的總程序運用了遞歸的思想,這樣達到了將程序分解的目的,同時也體現了分治的思想
c++語言實現函數(僅有函數部分,未經過測試)
1 const int maxn= 100;
2 const int max = 100000;
3 int * Merge(int * &A,int p,int q,int r)
4 {
5 int L[maxn]={0};
6 int R[maxn]={0};
7 int n1=q-p+1;
8 int n2=r-q;
9 for(int i=0;i<n1;i++)
10 L[i]=A[p+i-1];
11 for(int j=0;j<r;j++)
12 R[j]=A[q+j];
13 L[n1+1]=max;
14 R[n2+1]=max;
15 int a=1,b=1;
16 for(int i=p;i<r;i++)
17 {
18 if(L[a]<=R[b])
19 {
20 A[i]=L[a];
21 a++;
22 }
23 else
24 {
25 A[i]=R[b];
26 b++;
27 }
28 }
29 return A;
30 }
31
32 void MergeSort(int * &A,int p,int r)
33 {
34 if(p<r)
35 {
36 int q=(p+r)/2;
37 MergeSort(A,p,q);
38 MergeSort(A,q+1,r);
39 Merge(A,p,q,r);
40 }
41 }
算法分析
與遞歸算法相比較,在n大於30后,歸並算法有着較為明顯的優勢。他的時間復雜度比遞歸的時間復雜度更低,所以在排序較多數據時,歸並算法能取得較好的效果!
遞歸算法:Θ(n) = (n^2)
歸並算法:Θ(n) = (nlgn) //注意,這里的lgn是log2n的代表
下面我們用一張圖來說明其時間復雜度

假設問題的個數正好是2的n次冪
那么一層一層分下來
第一層的總代價cn
第二層總代價c(n/2) +c(n/2) = cn
………
這樣,每一層的總代價都是cn
那么一共有lgn層,再加上第一層
所以總代價即為:cn*lgn+cn = Θ(nlgn)
PS:即使問題的個數不是2的n次冪,也可使用這樣的算法,這里只是為了方便討論和理解