归并排序(Merging Sort)就是利用归并的思想实现的排序方法。它的原理是假设初始序列含有n个记录,则可以看成是n个有序的子序列,每个子序列的长度为1,然后两两归并,得到⌈n/2⌉(⌈x⌉表示不小于x的最小整数)个长度为2或1的有序子序列;再两两归并,……,如此重复,直至得到一个长度为n的有序序列为止,这种排序方法称为2路归并排序。
代码:
#define MAXSIZE 10 /* 用于要排序数组个数最大值,可根据需要修改 */
typedef struct
{
int r[MAXSIZE+1]; /* 用于存储要排序数组,r[0]用作哨兵或临时变量 */
int length; /* 用于记录顺序表的长度 */
}SqList;
/* 交换L中数组r的下标为i和j的值 */
void swap(SqList *L,int i,int j)
{
int temp=L->r[i];
L->r[i]=L->r[j];
L->r[j]=temp;
}
void MSort(int SR[],int TR1[],int s, int t);
void Merge(int SR[],int TR[],int i,int m,int n);
//对顺序表L作归并排序
void MergeSort(SqList *L)
{
MSort(L->r,L->r,1,L->length);
}
//将SR[s..t]归并排序为TR[s..t]
void MSort(int SR[],int TR1[],int s, int t)
{
int m;
int TR2[MAXSIZE+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..t]
}
}
//将有序的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;l++)
TR[k+1] = SR[i+1];//将剩余的SR[i..m]复制到TR
}
if (j <= n)
{
for (l=0;l<=n-j;l++)
TR[k+1] = SR[j+1];//将剩余的SR[j..n]复制到TR
}
}
复杂度分析:
我们来分析一下归并排序的时间复杂度,一趟归并需要将SR[1]~SR[n]中相邻的长度为h的有序序列进行两两归并。并将结果放到TR1[1]~TR1[n]中,这需要将待排序序列中的所有记录扫描一遍,因此耗费O(n)时间,而由完全二叉树的深度可知,整个归并排序需要进行⌈log以2为底对n取对数⌉次,因此,总的时间复杂度为O(nlogn),而且这是归并排序算法中最好、最坏、平均的时间性能。
由于归并排序在归并过程中需要与原始记录序列同样数量的存储空间存放归并结果以及递归时深度为log2n的栈空间,因此空间复杂度为O(n+logn)。 另外,对代码进行仔细研究,发现Merge函数中有if (SR[i]<SR[j])语句,这就说明它需要两两比较,不存在跳跃,因此归并排序是一种稳定的排序算法。
也就是说,归并排序是一种比较占用内存,但却效率高且稳定的算法。