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

来源:互联网 发布:微信小说系统源码 编辑:程序博客网 时间:2024/05/22 00:09

七、归并排序

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

1 0
原创粉丝点击