1、思想
采用分治法:将原问题分解成n个规模较小而结构与原问题相似的子问题;递归地解决这些子问题;合并其结果,即得解。
2、伪代码
分很简单,就是把n个元素分成n/2个元素的子序列。重点是合并,为了避免检查是否每个堆都是空的,我们在每一堆的底部放一个哨兵,它包含一个特殊的值∞。
合并的伪代码如下:
1 //合并已排好序(升序)的字数组A[p..q]和A[q+1..r],为A[p..r] 2 MERGE( A,p,q,r ) 3 n1 = q-p+1; 4 n2 = r-q; 5 //先把两个数组copy一份 6 create arrays L[1..n1+1], R[1..n2+1]; 7 for( i=1; i<=n1; i++ ) 8 do L[i] = A[i+p-1]; 9 for( i=1; i<=n2; i++ ) 10 do R[i] = A[i+q]; 11 L[n1+1] = ∞; 12 R[n2+1] = ∞; 13 //合并 14 i=1; 15 j=1; 16 for( k=p; k<=r; k++ ) 17 do if (L[i]<R[j]) 18 then A[k] = L[i]; 19 i++; 20 else A[k] = R[j]; 21 j++;
合并算法:
1 MERGE-SORT( A,p,r ) 2 if p<r 3 then q = (p+r)/2; 4 MERGE-SORT( A,p,q ); 5 MERGE-SORT( A,q+1,r ); 6 MERGE( A,p,q,r );
3、程序
//merge a[m..n] and a[n+1..q] void merge( int a[], int m, int n, int q ){ int n1,n2,i,j; n1 = n-m+1; n2 = q-n; int b[n1+1],c[n2+1]; for( i=0; i<n1; i++ ) b[i] = a[m+i]; for( i=0; i<n2; i++ ) c[i] = a[n+i+1]; b[n1] = INFINITE; c[n2] = INFINITE; i=j=0; for( int k=m; k<=q; k++ ){ if( b[i]<c[j] ){ a[k] = b[i]; i++; } else{ a[k] = c[j]; j++; } } } void MergeSort( int a[], int n ){ if( n<2 ) return; int m = n/2; MergeSort(a,m); MergeSort(a+m,n-m); merge(a, 0,m-1,n-1); }
4、时间分析
采用递归树的方式,树深度为lgn,所以时间代价为O(nlgn)。
4、对比插入算法
因为有常熟因子的影响,插入算法中的常熟因子要远远小于合并算法的常数因子,所以,如果是小规模的算法排序,插入排序要优于合并排序。但若规模扩大时,运行时间增长量级影响更大,此时,合并排序远优于插入排序。