归并排序(Merging Sort)就是利用归并(合并,并入的意思)的思想实1现的排序方法。它的原理是假设初始序列有n个记录,则可以看成是n个有序的子序列,每个子序列的长度为1,然后两两合并,得到n/2个长度为2或1的有序子序列;再两两归并,.........,如此重复,直至得到一个长度为n的有序序列为止,这种排序方法称为2路归并排序。
为了更清楚的地说明这里的思想,我们将本是无序的数组序列{16,7,13,10,9,15,3,2,5,8,12,1,11,4,6,14},通过两两合并排序后再合并,最终获得了一个有序的数组。如下图,注意观察它的图形,你会发现它很像一棵倒置的完全二叉树,通常涉及到完全二叉树结构的排序算法,效率一般都不低(如我们前面说过的堆排序)。
一、归并排序算法
下面举个具体的例子来说明归并排序的整个过程:
初始序列SR与TR1都来说明是{50,10,90,30,70,40,80,60,20},最终我们的目的就是要将TR1中的数组排好顺序。
我们最终是想要通过将子序列长度为1的归并为2,然后是2排序归并为4,.......所以一开始的第一步是将整个数组先划分为长度为1的每个单元,用二分逐步划分。
将SR中的第1~5的关键字归并到有序的TR2中(一开始为空),将第6~9关键字归并到有序的TR2中
TR2的左半部分先递归往下划分,如左图,然后在递归回溯的过程中两两排好序后合并
同样R2的右半部分先递归往下划分,如左图,然后在递归回溯的过程中两两排好序后合并
然后再将TR2的左半部分和右半部分合并到TR1中
整个过程如下图:
C++代码示意
// 将SR[s.....t]归并排序为TR1[s....t]
void MSort(int SR[],TR1[],int s,int t){
int m;
int TR2[length+1];
if(s==t)
TR1[S]=SR[S];
else{
m=(s+t)/2;//将 SR[s...t]平分为SR[s...m]和SR[m+1...t]
MSort(SR,TR2,s,m);//递归将 SR[s...m]归并为有序的TR2[s...m]
MSort(SR,TR2,m+1,t);//递归将 SR[m+1...t]归并为有序的TR2[m+1...t]
Merge(TR2,TR1,s,m,t);// 将TR2[s...m]和TR2[m+1...t]归并到TR1[s...m]
}
}
Merge函数的实现:
//将有序的SR[i...m]和SR[m+1...n]归并为有序的TR[i....n]
void Merge(int SR[],int TR[],int i,int m,int n){
int j,k,l;
for(j=m+1,k=i;i<=m&&j<=n;k++){ //将SR中记录由小到大归并入TR
if(SR[i]<SR[j])
TR[K]=SR[i++];
else
TR[K]=SR[j++];
}
if(i<=m){
for(l=0;l<=m-i;l++)
TR[K+l]=SR[i+l]; //将剩余的SR[i...m]复制到TR
}
if(j<=n){
for(l=0;l<=n-j;l++)
TR[K+l]=SR[j+l]; //将剩余的SR[i...m]复制到TR
}
}
二、归并算法复杂度分析
一趟归并需要将SR[1]~SR[n]中相邻的程度为h的有序序列进行两两归并。并将结果放到TR[1]~TR[n]中,这需要将待排序的的所有记录扫描一遍,因此耗费O(n)的时间,而由完全二叉树的深度可知,整个归并排序需要log2n次,因此总的时间复杂度为O(nlogn),而这时归并排序中最好,最坏、平均的时间性能。
由于归并排序在归并过程中需要与原始记录序列同样数量的存储空间存放归并结果以及递归时深度为log2n的栈空间,因此空间复杂度为O(n+logn)
归并排序是一种比较占用内存,但是效率高而且稳定的算法。
排序方法 | 平均情况 | 最好情况 | 最坏情况 | 辅助空间 | 稳定性 |
归并排序 | O(nlogn) | O(nlogn) | O(nlogn) | O(n) | 稳定 |