16 - 12 - 18 十大排序算法总结(三) 之 归并排序

七、归并排序

堆排序的效率确实高,充分利用了完全二叉树的深度是(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),并且避免递归也在时间性能上有一定的提升。应该说,使用归并排序时,尽量考虑用非递归方法。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值