归并排序(Merge Sort)

归并排序是一种基于分治法的高效排序算法,具有稳定性。本文详细介绍了归并排序的原理,包括如何通过`mergeSort`函数进行分解和`merge`函数进行合并,并提供了C++实现代码。此外,还进行了不同场景下的性能测试,如与插入排序对比、基本有序数组及含有大量重复元素的数组测试,展示了其在各种情况下的优秀性能。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

归并排序

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


  1. 分治法
    可以通俗的解释为:把一片领土分解,分解为若干块小部分,然后一块块地占领征服,被分解的可以是不同的政治派别或是其他什么,然后让他们彼此异化。
    分治法的精髓:
    分–将问题分解为规模更小的子问题;
    治–将这些规模更小的子问题逐个击破;
    合–将已解决的子问题合并,最终得出“母”问题的解;

归并排序
我们经过看这个过程图来了解归并排序。

这里写图片描述

  • 首先,是进行归并的“分”,即__mergeSort(T arr[], int l, int r),将整个数组分成左右两部分。
    int mid = (l + r) / 2; //arr[l...r]

再将左右两部分分别分成左右两部分,直到分成单个元素。

  • 第二步,是进行归并的“并”,即__merge(T arr[], int l, int mid, int r),递归的进行一步步向上的左右两部分的排序,也就是将arr[l…mid]和arr[mid+1…r]两部分进行归并。
    在这里要开辟一个辅助空间,即
T *aux = new T[r - l + 1];

将每次的两部分排序过程在aux空间中进行。


C++完整代码如下:

//归并排序


// 将arr[l...mid]和arr[mid+1...r]两部分进行归并
template<typename  T>
void __merge(T arr[], int l, int mid, int r)
{

    // 经测试,传递aux数组的性能效果并不好
    T *aux = new T[r - l + 1];
    for (int i = l; i <= r; i++)
        aux[i - l] = arr[i];

    int i = l, j = mid + 1;
    for (int k = l; k <= r; k++){

        if (i > mid)
        {
            arr[k] = aux[j - l];
            j++;
        }
        else if (j > r)
        { 
            arr[k] = aux[i - l];
            i++;
        }
        else if (aux[i - l] < aux[j - l])
        {
            arr[k] = aux[i - l];
            i++;
        }
        else
        {
            arr[k] = aux[j - l];
            j++;
        }
    }
    delete[] aux;
}

// 递归使用归并排序,对arr[l...r]的范围进行排序
template<typename T>
void __mergeSort(T arr[], int l, int r)
{

    if (r - l <= 15)   //这里的优化是当左右两部分分成小于等于16个时,用插入排序更快
    {
        insertionSort(arr, l, r);
        return;
    }

    int mid = (l + r) / 2; //arr[l...r]
    __mergeSort(arr, l, mid);
    __mergeSort(arr, mid + 1, r);
    if (arr[mid] > arr[mid + 1])    //这里是个小优化,在左部分的最后一个元素小于右部分第一个元素时即这个子数组已有序
    {
        __merge(arr, l, mid, r);
    }
}

template<typename T>
void mergeSort(T arr[], int n)
{

    __mergeSort(arr, 0, n - 1);
}

性能测试

算法分析
(1)稳定性
 归并排序是一种稳定的排序。
(2)存储结构要求
 可用顺序存储结构。也易于在链表上实现。
(3)时间复杂度
 对长度为n的文件,需进行 趟二路归并,每趟归并的时间为O(n),故其时间复杂度无论是在最好情况下还是在最坏情况下均是O(nlgn)。

测试1(与插入排序相比)

int n = 100000;
    cout << "Test for Random Array, size = " << n << ", random range [0, " << n << "]" << endl;
    int *arr1 = SortTestHelper::generateRandomArray(n, 0, n);
    int *arr2 = SortTestHelper::copyIntArray(arr1, n);

    SortTestHelper::testSort("Insertion Sort", insertionSort, arr1, n);
    SortTestHelper::testSort("Merge Sort", mergeSort, arr2, n);

测试数据为无序的10万随机数的数组。
结果如下:
这里写图片描述


测试2


    int swapTimes = 100;
    cout << "Test for Random Nearly Ordered Array, size = " << n << ", swap time = " << swapTimes << endl;
    arr1 = SortTestHelper::generateNearlyOrderedArray(n, swapTimes);
    arr2 = SortTestHelper::copyIntArray(arr1, n);


    SortTestHelper::testSort("Insertion Sort", insertionSort, arr2, n);
    SortTestHelper::testSort("Merge Sort", mergeSort, arr2, n);

10万个基本有序的数组测试

结果如下:
这里写图片描述


测试3

cout << "Test for Random Array, size = " << n << ", random range [0,10]" << endl;
    arr1 = SortTestHelper::generateRandomArray(n, 0, 10);
    arr2 = SortTestHelper::copyIntArray(arr1, n);


    SortTestHelper::testSort("Insertion Sort", insertionSort, arr2, n);
    SortTestHelper::testSort("Merge Sort", mergeSort, arr2, n);

10万个含有大量重复元素的数组

结果如下:
这里写图片描述

结果很明显啦

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值