归并排序

本文介绍了一种高效排序算法——归并排序,详细解释了其基于二分法的递归思想及时间复杂度,并通过C++代码实现了算法逻辑,对比了其与插入排序的时间性能。

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

  归并排序是一种时间赋值度为O(nlogn)级别的算法,其基本思想就是利用二分法将一个待排序列逐层向下划分,直到每一组中只有1个数据为止,然后将这些只有1个数据的序列都视为有序序列,逐层向上归并成有序序列。

  根据二分法的思想可知,一个含有n个数据的序列,可以向下划分为logn个层级,而在每个层级上若可以使用时间复杂度为O(n)的算法来实现归并,则归并排序算法的时间复杂度即为O(nlogn)。

  将有序序列两两向上归并时,采用的是一种递归思想,直到最后将序列的左右半有序序列归并成一个有序表,算法结束。



  对于两个有序表的归并的图解如下:


  归并排序算法c++代码如下:

  SortTestHelper.h文件(辅助函数)

#include <iostream>
#include <cstdlib>
#include <ctime>  //clock()、CLOCKS_PER_SEC
#include <cassert>  //包含函数assert()

using namespace std;

namespace SortTestHelper
{
    //辅助函数 - 随机产生一个数组
    int* generateRandomArray(int n, int RangeL, int RangeR)  //返回数组首地址
    {
        //判断RangeL是否<=RangeR
        assert(RangeL <= RangeR);  //参数为表达式,表达式为真时返回true,否则打印错误信息

        int *arr = new int[n];
        srand(time(0));
        for(int i = 0; i < n ; i++)
        {
            arr[i] = rand() % (RangeR - RangeL + 1) + RangeL;  //使得产生的随机数在RangeL和RangeR之间
        }
        return arr;
    }

    //辅助函数 - 产生一个近乎有序的随机数组
    int* generateNearlyOrderedArray(int n, int swapTime)
    {
        int *arr = new int[n];
        for(int i = 0; i < n; i++)
        {
            arr[i] = i;  //先生成一个完全有序的数组
        }
        //然后交换几组元素,使之变成无序但近乎有序的数组
        srand(time(0));
        for(int j = 0; j < swapTime; j++)
        {
            //随机生成一个x位置和y位置
            int posx = rand() % n;
            int posy = rand() % n;
            //交换x和y处的元素
            swap(arr[posx], arr[posy]);
        }
        return arr;
    }

    //辅助数组 - 产生一个完全有序数组
    int* generateTotallyOrderedArray(int n)
    {
        int *arr = new int[n];
        for(int i = 0; i < n; i++)
        {
            arr[i] = i;
        }
        return arr;
    }

    //辅助函数 - 打印数组
    template<typename T>
    void printArray(T arr[], int n)
    {
        for(int i = 0; i < n; i++)
        {
            cout << arr[i] << " ";
        }
        cout << endl;
    }

    //辅助函数 - 判断数组是否有序(升序)
    template<typename T>
    bool isSorted(T arr[], int n)
    {
        for(int i = 0; i < n - 1; i++)
        {
            if(arr[i] > arr[i + 1])
            {
                return false;
            }
        }
        return true;
    }

    //辅助函数 - 测试算法的时间
    template<typename T>
    void testSort(string sortname, void(*sort)(T[], int), T arr[], int n)  //arr[]和n是函数指针需要的参数
    {
        clock_t starttime = clock();
        sort(arr, n);  //调用函数sort()
        clock_t endtime = clock();

        //判断排序是否成功
        assert(isSorted(arr, n));  //若是数组无序,则assert会自动调用abort()退出程序,不会执行下面的语句

        cout << sortname << " needs " << double(endtime - starttime) / CLOCKS_PER_SEC << "s." << endl;
    }

    //辅助函数 - 拷贝数组
    int* copyIntArray(int a[], int n)
    {
        int *arr = new int[n];
        //使用C++函数copy()
        copy(a, a + n, arr);
        return arr;
    }
}

main.cpp文件(归并算法、插入算法,测试两者的时间性能)

#include <iostream>
#include "SortTestHelper.h"

/**
 * 归并排序(递归过程):将有n个记录的数组采用二分法,直到每组只有1个记录(有序),然后再将有序序列两两归并;
 */

using namespace std;

template<typename T>
void __merge(T arr[], int l, int mid, int r)
{
    //将左半部分arr[l...mid]和右半部分arr[mid + 1...r]进行归并
    //辅助空间 - arr[l...r]的拷贝
    T aux[r - l + 1];
    for(int i = l; i <= r; i++)  //这里的i是arr中的下标
    {
        //aux数组用于归并时用,归并结果存放在arr中
        aux[i - l] = arr[i];  //将arr[l...r]拷贝给aux[0...r-l]
    }
    int i = l, j = mid + 1;  //i,j是arr中的下标
    for(int k = l; k <= r; k++)  //k是arr中存放较小元素的位置下标的一个位置
    {
        //考虑数组是否越界(因为递归时已判断l < r,所以此处无需再判断l和r的大小)
        if(i > mid)
        {
            arr[k] = arr[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++;
        }
    }
}

template<typename T>
void __mergeSort(T arr[], int l, int r)  //一定要注意我设定的是左闭右闭[l...r](r是最后一个元素)
{
    //递归使用归并排序,对arr[l...r]进行归并
    //先考虑递归最底层
    if(l >= r)
    {
        return;
    }
    else
    {
        int mid = (l + r) / 2;
        //对左半部分arr[l...mid]进行归并排序
        __mergeSort(arr, l, mid);
        //再对右半部分arr[mid + 1...r]进行归并排序
        __mergeSort(arr, mid + 1, r);
        //然后将排好序的左右两部分归并到一起
        __merge(arr, l, mid, r);
    }
}

template<typename T>
void mergeSort(T arr[], int n)
{
    //传递一个数组,调用归并排序算法归并arr[0...n-1]
    __mergeSort(arr, 0, n - 1);
}

//插入排序
template<typename T>
void insertionSort(T arr[], int n)
{
    for(int i = 1; i < n; i++)  //0号单元已经有序
    {
        T e = arr[i];  //拷贝待插入元素,类型为T
        //寻找arr[i]的合适的插入位置 - 循环
        int j;  //j就是插入位置的下标
        for(j = i; j > 0 && arr[j - 1] > e; j--)
        {
            arr[j] = arr[j - 1];
        }
        //此时插入位置为j
        arr[j] = e;
    }
}

int main()
{
    int n = 10000;
    int *arr = SortTestHelper::generateRandomArray(n, 0, n);
    int *arr2 = SortTestHelper::copyIntArray(arr, n);
    SortTestHelper::testSort("mergeSort", mergeSort, arr, n);
    SortTestHelper::testSort("insertionSort", insertionSort, arr2, n);
    delete[] arr;
    delete[] arr2;
    return 0;
}
  归并排序算法需要O(n)的辅助空间,而插入算法不需要辅助空间,但是一个算法的时间性能比空间性能重要的多,而从测试结果可以看到,归并算法的时间性能比插入算法好得多。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值