归并排序

二路归并排序

            1,递归思想

假设初始列表含有n个记录,则可以看成n个有序子序列,每个有序子序列的长度为1,然后两两归并,得到n/2个长度为2的有序子序列,再两两归并,如此重复下去,直到得到一个长度为n的有序序列为止。

            2,递归实现二路归并排序 
#define MAXSIZE 10  //定义一个宏,是arr的大小,也是需要开辟数组的大小

//二路归并,将TR2[m..t]和[t+1..n]归并成有序序列
void Merge(int TR1[], int TR2[],int m, int t, int n)
{
    int j,k;//j为TR1[]的下标,j为TR2[t+1..n]的下标,而m为TR2[m..t]的下标
    //将TR2中记录由小到大归并入TR1中
    for(k = m,j = t+1; m <= t && j <= n; ++k)
    {
        if(TR2[m] < TR2[j])
            TR1[K] = TR2[m++];
        else
            TR1[K] = TR2[j++];
    }
    //将剩余的TR2[m..t]复制到TR1中
    for(; m <= t; ++m,++k)
        { TR1[k] = TR2[m]; }
    //将剩余的TR2[t+1..n]复制到TR1中
    for(; j <= n; ++j,++k)
        { TR1[k] = TR2[j]; }
}
//该函数将大量数据递归下去,直到数据有序
void MSort(int SR[], int TR1[], int m, int n)
{
    int t; //中间值,来划分归并的前半部分和后半部分
    int TR2[MAXSIZE];//辅助数组,用来存储排归并排序后的有序子序列
    if(m == n)//递归终止条件  m==n时,此趟归并完成
        TR1[m] = SR[m];
    else
    {
        t = (m+n) / 2; //将SR分为[m..t]和[t+1..n]
        Msort(SR,TR2,m,t);//将SR[m..t]归并排序成有序数组放到TR2[m..t]
        Msort(SR,TR2,t+1,n);//将SR[t+1..n]归并排序成有序数组放到TR2[t+1..n]
        Merge(TR1,TR2,m,t,n);//最后将TR2中的[m..t]与[t+1..n]合并成有序数组放到TR1,则TR1保存的便是归并排序后的结果。
    }
}

//该函数只是封装了一个需要递归的函数
void MergeSort(int *arr, int n)
{
    assert(arr != NULL && n > 1);
    MSort(arr,arr,0,n-1);
}
        3,归并排序递归算法的时间复杂度 

时间复杂度为O(nlogn)
一趟排序需要将SR[0]~SR[n-1]中相邻的长度为h的有序子序列进行两两归并,并将结果放入TR1[0]~TR1[n-1]中,这需要将待排序序列扫描一遍,耗费时间为O(n),根据二叉树的性质,整个归并排序需要logn次,所以归并排序的时间复杂度为O(nlogn)。

空间复杂度为O(n+logn),存储交换结果的数组已经递归的栈空间。
归并排序特点:空间比较占内存,但排序效率高且稳定。

归并排序的递归算法,大量的引用的递归,尽管代码比较清晰,但其会消耗时间和空间上的性能,我们追求效率,所以让我们来看看迭代方法。

         4,归并排序的迭代实现 
#define MAXSIZE 10
//将SR[m..t]和[t+1..n]归并的结果放入TR中
void Merge(int SR, int TR,int m, int t, int n)
{
    int j,k;
    for(j = t+1,k = m;m <= t && j <= n; ++k)
    {
        if(SR[m] < SR[j])
            TR[k] = SR[m++];
        else
            TR[k] = SR[j++];
    }

    for(;m <= t; ++m,++k)
    {
        TR[k] = SR[m];
    }
    for(;j <= n; ++j,++k)
    {
        TR[k] = SR[j];
    }

}

//将相邻长度为s的子序列归并到TR中
void MergePass(int SR[], int TR[],int s, int n)
{
    int i = 0;
    int j;
    while(i <= n - 2*s +1)
    {
        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);
    }
    else  //归并最后剩下的单个子序列
    {
        for(j = i; j < n; ++j)
            TR[j] = SR[j];
    }
}

void NiceMergeSort(int *arr, int n)
{
    assert(arr != NULL && n > 1);
    int *TR = (int*) malloc(sizeof(int) * MAXSIZE);
    int k = 1; //k为归并长度  第一次将相隔为1的数据进行归并
    while(k < MAXSIZE-1)
    {
        //将原来无序的数列两两归并入TR
        MergePass(arr,TR,k,MAXSIZE-1);
        k *= 2;  //归并长度扩大一倍
        //将两两归并有序的数列,再次归并入数组arr中
        MergePass(TR,arr,k,MAXSIZE-1);
        k *= 2;
    }
}
        5,非递归迭代的复杂度

非递归迭代的方法,避免了递归时深度为logn的栈空间,空间上只申请了临时数组TR,空间复杂度为O(n),避免递归也在时间复杂度上有了一定的提升,因此,我们使用归并排序,应该多使用迭代方法。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值