归并排序

        归并排序是一种稳定的排序方法,它的中心思想是将两个或者两个以上的有序序列合成一个新的有序表,通过不断的合并得到最终的有序表。最常用的是将2个子序列不断的合并,也就是2路归并排序。

        归并排序要求待排序的子序列一定要有序,如果无序怎么办呢,那就把子序列不断的划分的更细,当子序列中只包含一个元素的时候,那么它肯定是有序的,此时把子序列两两归并排序,便可得到最终的有序序列。


归并排序有递归方法非递归方法


首先来看递归的归并排序:

//将两个有序的子序列归并为一个有序序列

//SR[]为待排序的序列,TR[]用于存放已经排序好的序列

//iSR[]序列的起始下标,m为第一个子序列结束时的下标,nSR[]的序列长度

void Merge(int SR[],int TR[],int i,int m,int n)

{

    int j = 0;  //j为第二个子序列的起始下标

    int k = 0;  //k用于记录TR[]序列已经排好的下标

    int l = 0;  //l用于排好一个子序列后另一个子序列的补充长度

    

    //第一个子序列和第二个子序列按位依次比较,把较小的数放入TR[]数组中,直到第一个或者第二个子序列结束跳出循环

    for(k = i, j = m +1; i <=m && j <=n; k++){

        if(SR[i] < SR[j]){

            TR[k] = SR[i];

            i++;

        }

        else{

            TR[k] =SR[j];

            j++;

        }

    }

    

    //比较完成后,如果第1个子序列长度有剩余,则将剩余的数据依次添加到TR[]

    if(i <= m)

    {

        for(l =0; l <= m - i; l++)

            TR[k + 1] = SR[i +1];

    }

    

    //如果第2个子序列长度有剩余,则将剩余的数据依次添加到TR[]

    if(j <= n)

    {

        for(l =0;l <= n - j; l++)

            TR[k + 1] = SR[j +1];

    }

}


有了将两个有序的子序列归并为一个有序序列的函数后,将一个无序序列进行归并排序的思路如下:
通过不断递归的调用MSort将无序的序列拆分至个体,变成部分有序,再将部分有序序列通过Merge不断整合,得到最终的有序序列。

//SR[]为待排序序列,TR1[]用于存放最后已经排序好的序列

//sSR[]需要排序的起始下标,tSR[]需要排序的终止下标

void MSort (int SR[],int TR[],int s,int t)

{

    int m;

    int TR2[t +1];                 //需要额外的t+1的内存空间记录数据

    if (s == t) {

        TR[s] = SR[s];

    }

    else

    {

        m = (s +t) / 2;            //SR[]平分为两部分

        MSort(SR, TR2, s, m);      //将前半部分继续递归划分至有序的TR2[](前半部分)

        MSort(SR, TR2, m +1, t);  //将后半部分继续递归划分至有序的TR2[](后半部分)

        Merge(TR2, TR, s, m, t);  //将部分有序的TR2[]整合成一个有序的TR1[]

    }

}


递归的归并排序复杂度分析:
时间复杂度:将SR[n]进行两两归并成为部分有序需要将所有元素扫描一遍,耗费O(n)时间, 由完全二叉树的性质可知,一共需要扫描log(n)次(结点为n的完全二叉树深度),因此时间复杂度为O(nlogn),无论初始序列有序度如何,都不影响归并排序的时间复杂度。

空间复杂度:归并排序过程中需要与原始数据相同的存储空间(TR2[]),以及递归的深度logn的空间,因此空间复杂度为O(n + logn)

非递归的归并排序:
非递归的归并排序看起来更直接一些,他的基本思想是直接把待排序数组两两归并成有序序列,不用从一开始递归拆分了

//传入参数为待排序的序列SR[]以及序列长度length

void MergeSort(int SR[],int length)

{

    int TR[length +1];                //申请一块额外的内存空间

    int k =1;                         //k代表每次进行两两归并的数据长度,肯定是2的倍数

    while (k < length) {

        MergePass(SR, TR, k, length);  //SR的数据两两归并到TR

        k = k * 2;

        MergePass(TR, SR, k, length);  //再把TR的数据归并会SR,就是两块内存来回用

        k = k * 2;

    }

}


//SR的数据归并到TR,归并的子长度为s,数据总长度为length

void MergePass(int SR[],int TR[],int s,int length)

{

    int i =1;

    int j;

    while(i <= length - (2 * s -1))             //判断如果数组中的数据为奇数,则最后一个数据不做归并处理

    {

        Merge(SR, TR, i, i + s -1, i +2 * s -1);//第一个子序列起始下标为i,结束下标为i + s - 1

        i = i + 2 * s;

    }

    

    if (i < length - s +1)         //说明剩下的不是一个子序列(此处是一个长度为s序列一个长度小于s的序列)

    {

        Merge(SR, TR, i, i + s -1, length);  //对这两个子序列进行归并

    }

    else                                  //如果只剩了一个子序列

    {

        for (j = i; j <= length; j++)     //直接把子序列复制到TR

        {

            TR[j] = SR[j];

        }

    }

}



非递归的迭代方法,避免了递归深度为logn的栈空间,因此空间复杂度为O(n)时间复杂度仍为O(nlogn)
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值