归并排序及其变体

归并排序(Merge Sort)

一般指二路归并排序,是稳定排序,原理如下:

归并排序分为两个过程,一是通过递归不断将数组二分,直到无法再二分进行第二步:合并,具体如图:

在这里插入图片描述

二分过程通过递归很好实现,合并过程的详细过程为:

首先要明确两个事实:一是这两个要合并的序列不一定等长(数组总长度不可能总是2的次方),但这并不妨碍合并过程;二是因为通过递归,每个要合并的序列本身已经是有序的了。

合并过程如下:另开一个数组c,存储合并后的有序(这里以降序排序为例)序列,设两个待合并序列为a和b,对a[1]与b[1]进行比较,若a[1]更大,则将a[1]放入c[1],a、c指针后移。然后比较a[2]与b[1],较大者放入c[2]并移动指针,以此类推。当a或b中全部数字移入c中,可将另一序列中剩余数字按原顺序一次性移入。最后将数组c中的有序序列移回原数组。

递归写法
//lef为前一个序列的首坐标,rig为后一个序列的首坐标
void JustMerge(int* a, int n, int lef, int rig, int step){
    int b[step*2], len = 0; 
    int i = lef, j = rig;
    int m = n<rig+step ? n : rig+step; 
    //当后一个序列的长度不足step时,防止越界
    while (i<rig && j<m){
        if (a[i]<a[j]) 
            b[len++] = a[i++];
        else 
            b[len++] = a[j++];
    }

    while (i<rig)
        b[len++] = a[i++];
    while (j<m)
        b[len++] = a[j++];    
 
    for (int k=lef; k<m; k++) 
        a[k] = b[k-lef];
}

//指定排序范围:从a的第i项到第j项
void MergeSort(int *a, int n, int i, int j){
    if (i == j) return; 
    int mid = i + (j-i)/2;
    MergeSort(a, n, i, mid);
    MergeSort(a, n, mid+1, j);    
    JustMerge(a, n, i, mid+1, mid+1-i);
}
非递归写法

通过上图可知,一开始合并时,每个序列长度为1(仅一个数字),然后当所有长度为1的序列两两合并后,每个待合并序列长度为2,然后是4、8、16……因此,我们可以省去递归二分的过程,直接通过上述规律定相应数组下标进行合并。

void MergeSort(int *a, int n) {
    int step = 1, lef, rig; 
    //lef为前一个序列的首坐标,rig为后一个序列的首坐标
    while (step < n){
        lef = 0;
        while (lef<n) {
            rig = lef + step;
            if (rig>=n)
                break;
            JustMerge(a, n, lef, rig, step);
            lef = rig + step;
        }
        step *= 2;
    }
}



参考资料及部分图片来源

归并排序 —— 递归实现 + 非递归实现

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值