七、归并排序
堆排序的效率确实高,充分利用了完全二叉树的深度是(log2n+1)(下取整)的特性。
所以效率较高,但是其本身的构建变焦复杂,那有没有更简单的(完全二叉树)方法呢.
ex:{16,7,13,10,9,15,3,2,5,8,12,1,11,4,6}两两归并:!
上图 ↑ 就是一棵倒立的二叉树。
直观排序效果 ↓ :
// 归并排序,使用递归
void MergeSort(SqList *L){
MSort(L->r, L ->r, 0, L->length-1);
}
// 将SR[s..t]归并排序为TR1[s..t]
void MSort(int SR[],int TR1[],int s,int t) //L->r, L-r, 0, L->length-1 是实参
{
int m;
int TR2[MAXSIZE];
if (s == t)
TR1[s] = SR[s];
else
{
// 将SR[s..t](r[0~length-1])平分为SR[s...m-1]和SR[m...t] (r[1~m] & r[m~length-1])
m = (s + t) / 2+1;
MSort(SR, TR2, s, m-1);
MSort(SR, TR2, m, t);
// 将TR2[s..m-1]和TR2[m..t]归并到TR1[s..t]
Merge(TR2, TR1, s, m-1, 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 - i; l++)
// 将剩余的SR[i..m]复制到TR
TR[k + l] = SR[i + l];
}
if (j <= n){
for (l = 0; l <= n - j; l++)
// 将剩余的SR[j..n-1]复制到TR
TR[k + l] = SR[j + l];
}
}
归并性功能分析:
总的时间复杂度(最好,最坏,平均都是)为:O(n*logn);
由于归并排序在归并过程中需要与原序列一样的空间来
存放归并结果、以及递归时深度为log2n的 栈空间,因此
空间复杂度为O(n+logn)
另外,归并排序是使用两两比较,不存在跳跃,
这在Merge()中的语句 if ( SR[i] 小于 SR[j] ) 可以看出
所以归并排序是一个稳定的排序算法。
总体来说,归并排序是一个比较占用内存,但效率高且稳定的算法。
下面介绍一个非递归版本的归并排序算法实现。
// 非递归版本的归并排序
void MergeSort2(SqList *L){
// 申请额外空间
int* TR = (int *)malloc(L->length * sizeof(int));
int k = 1;
while (k < L->length){
MergePass(L->r, TR, k, L->length);
// 子序列长度加倍
k = 2 * k;
MergePass(TR, L->r, k, L->length);
k = 2 * k;
}
}
// 将SR[]中相邻长度为s的子序列两两归并到TR[]
void MergePass(int SR[], int TR[], int s, int n){
int i = 0;
int j;
while (i <= n - 2 * s){
// 两两归并
Merge(SR, TR, i, i + s - 1, i + 2 * s - 1);
i = i + 2 * s;
}
if (i < n - s + 1)
// 归并最后两个子序列
Merge(SR, TR, i, i + s - 1, n - 1);
else{
// 若最后剩下单个子序列
for (j = i; j <= n - 1; j++)
TR[j] = SR[j];
}
}
// 将有序的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++)
// 将剩余的SR[i..m]复制到TR
TR[k + l] = SR[i + l];
}
if (j <= n){
for (l = 0; l <= n - j; l++)
// 将剩余的SR[j..n-1]复制到TR
TR[k + l] = SR[j + l];
}
}
非递归版本的归并排序算法避免了递归时深度为log2nlog2n的栈空间,空间复杂度是O(n)O(n),并且避免递归也在时间性能上有一定的提升。应该说,使用归并排序时,尽量考虑用非递归方法。