经典排序算法2:归并排序

本文主要介绍归并排序的基本原理,基于递归调用,一步一步分析,结合流程图和文字,给读者清晰地解释。另外给出伪代码,和C++核心代码。并和插入排序相比较,最后结合插入排序,介绍一种升级的归并排序。

基本原理

归并排序利用分治法的思想,具体算法框架如下:
step1: 将待排序列 A 分为两个子序列,再将子序列一分为二,一直分到每个子序列只含有一个元素为止,这个时候,每个子序列(都只包含一个元素)已经是有序的。
step2: 根据分解的路径,对每一对子序列进行排序
step3: 将已经排序的两个子序列合并,最后合成整个序列。

分解示意图:
这里写图片描述
合并示意图:
这里写图片描述
具体的合并方式为:将两个已经排好序的子序列合并为一个新的长序列。从左到右,依次浏览两个待合并序列的第一个元素,将较小的元素移入到新序列中,当其中一个序列全部排到新序列里,则将另一个序列直接复制到新序列的末尾

伪代码

两个子序列合并的伪代码如下:

这里写图片描述
整个归并排序是一个递归过程,不断解决规模较小的子问题,然后将这些子问题的解合并得到原问题的解。伪代码如下。
这里写图片描述

本文给出的实例,递归调用步骤:
这里写图片描述

C++ 代码:

void myMerge(vector<int> & vec, int low, int mid, int high)
{
    vector<int> vecLow, vecHigh;
    int locL = 0, locH = 0;
    int lengthLow = mid - low + 1, lengthHigh = high - mid;
    int loc1;
    for (loc1 = low; loc1 <= mid; loc1++)
    {
        vecLow.push_back(vec[loc1]);
    }
    for (; loc1 <= high; loc1++)
    {
        vecHigh.push_back(vec[loc1]);
    }
    int loc;
    for (loc = low; loc <= high; loc++)
    {
        if (vecLow[locL] <= vecHigh[locH])
        {
            vec[loc] = vecLow[locL];
            locL ++;
            if (locL == lengthLow)break;

        }
        else
        {
            vec[loc] = vecHigh[locH];
            locH ++;
            if (locH == lengthHigh)break;
        }
    }
    if (loc < high) // 没有排满
    {
        if (locL < lengthLow)
        {
            for (; locL < lengthLow ; locL++)
            {
                loc++;
                vec[loc] = vecLow[locL];
            }
        }
        else
        {
            for (; locH < lengthHigh ; locH++)
            {
                loc++;
                vec[loc] = vecHigh[locH];
            }
        }
    }
}
void mergeSort(vector<int> & vec, int low, int high)
{
    if (low < high)
    {
        int mid = (high + low) / 2;
        mergeSort(vec, low, mid);
        mergeSort(vec, mid + 1, high);
        myMerge(vec, low, mid, high);
    }
}

性能分析

复杂度分析

由于将两个子数组合并成长度为 n 的子数组需要的时间为 Θ(n) ,根据合并示意图,假设序列长度为 N ,则递归调用 Merge 函数的级数为 log2(N) 向上取整,所以总的时间代价为 Θ(Nlog2(N))
根据上一篇 插入排序 的时间复杂度为 Θ(n2) ,归并排序优于插入排序。
由于归并排序在归并过程中需要与原始记录序列相等的存储空间,另外递归调用时需要深度为 log2(N) 的栈空间,因此空间复杂度为 c1n+c2log2(N) ,其中 c1,c2 为与系统相干的常数。即空间复杂度为 O(n) 。而插入排序不需要建立待排序列的副本,即不需要额外的存储空间,因此,在空间复杂度方面,插入排序占优势。

稳定性分析:

根据合并的具体操作,当两个数相等时,它们的相对位置不会改变,因此归并排序具有稳定性。

升级版:(结合插入排序)

由于当 n 较小时,n2 log2n 相差不大,为了减小递归调用的开销,当子序列减小到一定的规模时,可以使插入排序,然后将插入排序后的两个子序列合并。
具体C++代码如下:

void insertSort1(vector<int> &vec,int low,int high)
{
    int subVecSize = high - low + 1;
    int j;
    for (int i = low + 1; i <= high; i++) // i 表示已经排好序的子数组大小减一
    {
        int insertValue = vec[i]; // 将这个值插入已经排好序的数组
        for (j = i - 1; j >= low; j--)
        {
            if (insertValue >= vec[j])
                break; // 找到了要插入 insertValue 的位置 j
            vec[j + 1] = vec[j];
        }
        vec[j + 1] = insertValue;
    }
}

//当子序列的长度小于或等于 n0 时用插入排序
void mergeSort1(vector<int> & vec, int low, int high, int n0)
{
    if (low < high - n0 + 1)
    {
        int mid = (high + low) / 2;
        mergeSort1(vec, low, mid,n0);
        mergeSort1(vec, mid + 1, high,n0);
        myMerge(vec, low, mid, high);
    }
    else
    {
        insertSort1(vec, low, high);
    }
}

插入排序,归并排序,升级版归并排序 分别用运行时间比较:
输入序列为 10000 点的随机序列, 当子序列长度小于等于 n0=64 时, 用插入排序。
这里写图片描述
由此可见,归并排序的时间远远小于插入排序,主要原因由于插入排序需要大量的元素搬移操作,而且升级版的归并排序略好于普通版的归并排序。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值