归并排序基本原理及实现

陌路散爱 提交于 2020-03-28 04:29:09

一、归并(Merge)

1. 概念

将两个有序数列合并成一个有序数列,我们称之为“归并”。

 

2. 算法思路及实现

设两个有序的子序列(相当于输入序列)放在同一序列中相邻的位置上:array[low..m],array[m + 1..high],先将它们合并到一个局部的暂存序列 temp (相当于输出序列)中,待合并完成后将 temp 复制回 array[low..high]中,从而完成排序。

在具体的合并过程中,设置 i,j 和 p 三个指针,其初值分别指向这三个记录区的起始位置。合并时依次比较 array[i] 和 array[j] 的关键字,取关键字较小(或较大)的记录复制到 temp[p] 中,然后将被复制记录的指针 i 或 j 加 1,以及指向复制位置的指针 p 加 1。重复这一过程直至两个输入的子序列有一个已全部复制完毕(不妨称其为空),此时将另一非空的子序列中剩余记录依次复制到 array 中即可。

c语言实现如下:

void merge(int* array, int low, int mid, int high)
{
    assert(array && low >= 0 && low <= mid && mid <= high);

    int* temp = (int*)malloc((high - low + 1) * sizeof(int));
    if (!temp) {
        printf("Error:out of memory!");
        return;
    }

    int i = low;
    int j = mid + 1;
    int index = 0;

    while (i <= mid && j <= high) {
        if (array[i] <= array[j]) {
            temp[index++] = array[i++];
        }
        else {
            temp[index++] = array[j++];
        }
    }

    while (i <= mid) {
        temp[index++] = array[i++];
    }

    while (j <= high) {
        temp[index++] = array[j++];
    }

    memcpy((void*)(array + low), (void*)temp, (high - low + 1) * sizeof(int)) ;

    free(temp);
}

 

二、 归并排序(Merge Sort)概念

建立在归并操作上的一种排序算法,该算法是采用分治法(Divide and Conquer)的一个非常典型的应用。

归并排序有多路归并排序、两路归并排序,可用与内排序,也可用于外排序,本文仅对内排序的两路归并方法进行讨论。

 

三、归并排序思路及实现

1. 从下往上的归并排序

  • 把n个记录看成n个长度为1的数列(因为长度为1,所以自然有序)。
  • 进行两两归并,得到n/2个长度为2的有序数列,再将这些数列两两合并,得到n/4个长度为4的有序数列,重复这个过程,直到合并成一个数列为止。

过程如下图所示:

merge-sort-animation

C语言实现:

// 对 [0, length - 1] 做一趟归并长度为 n  的归并排序
void merge_pass(int* array, int length, int n)
{
    assert(array && length >= 1 && n >= 1);

    int i;
    int sortLength = 2 * n;

    // 归并长度为 n 的两个相邻子序列
    for(i = 0; i + sortLength - 1 < length; i = i + sortLength) {
        merge(array, i, i + n - 1, i + sortLength - 1);
    }

    // 若 i + n - 1 < length - 1,则剩余一个子文件轮空,无须归并。
    // 尚有两个子序列,其中后一个长度小于 n, 归并最后两个子序列。
    if (length - 1 > i + n - 1) {
        merge(array, i, i + n - 1, length - 1);
    }
}

// 用分治法自下向上进行二路归并排序
//
void merge_sort(int* array, int length)
{
    assert(array && length >= 0);

    int n;

    for(n = 1; n < length; n = (n << 1)) {
        merge_pass(array, length, n);
    }
}

优点是效率高,缺点是代码可读性差。

 

2. 从上往下的归并排序

具体的实现有三个步骤:

  • 分解 -- 将当前区间一分为二,即求分裂点 mid = (low + high)/2;
  • 求解 -- 递归地对两个子区间a[low...mid] 和 a[mid+1...high]进行归并排序。递归的终结条件是子区间长度为1。
  • 合并 -- 将已排序的两个子区间a[low...mid]和 a[mid+1...high]归并为一个有序的区间a[low...high]。

如下图所示:

151853346211212

C语言实现:

void merge_sort_dc_impl(int* array, int low, int high)
{
    assert(array && low >= 0);

    int mid;
    if (low < high) {
        mid = (low + high) >> 1;

        merge_sort_dc_impl(array, low, mid);
        merge_sort_dc_impl(array, mid + 1, high);

        merge(array, low, mid, high);
    }
}

// 用分治法自上向下进行排序
void merge_sort_dc(int* array, int length)
{
    assert(array && length >= 0);

    merge_sort_dc_impl(array, 0, length - 1);
}

 

四、时间复杂度及稳定性

1. 时间复杂度

长度为n的序列需要进行logn次二路归并才能完成排序(归并排序的形式其实就是一棵二叉树,需要遍历的次数就是二叉树的深度),而每趟归并的时间复杂度为O(n),因此归并排序的时间复杂度为O(nlogn)

 

2. 排序稳定性

所谓排序稳定性,是指如果在排序的序列中,存在两个相等的两个元素,排序前和排序后他们的相对位置不发生变化的话,我们就说这个排序算法是稳定的。

排序算法是稳定的算法。

 

五、参考

1. 排序算法之归并排序

2. 归并排序

3. 常见排序算法 - 归并排序 (Merge Sort)

(完)

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!