• 排序算法之归并排序


    排序算法之归并排序

    1.归并排序介绍

    归并排序的核心思想是分治法,是建立在归并操作上的一种有效的排序算法。通过递归的对半拆分成最小单元(一个元素),然后通过回溯的比较合并,构成整个归并的排序。

    分治法:将原问题分解为一些规模较小的相似子问题,然后递归解决这些子问题,最后合并其结果作为原问题的解。

    算法步骤:
    • 1.把数组递归拆分成N个最小数组单元
    • 2.回溯地对每个组进行合并排序
    • 3.把已有的排序好的组,两两合并,直到合并到组数为1。

    原数组: 2 1 8 4 10 3 9 7
    第一次递归分解: (2 1 8 4) (10 3 9 7)
    第二次递归分解: (2 1) (8 4) (10 3) (9 7)
    第三次递归分解: (2) (1) (8) (4) (10) (3) (9) (7)

    从最后回溯归并:
    第一次归并:(1 2) (4 8) (3 10) (7 9)
    第二次归并:(1 2 4 8) (3 7 9 10)
    第三次归并:(1 2 3 4 7 8 9 10)

    Ok, 经过递归分解和归并后,整个数组有序了。

    2.归并排序算法从实现到优化

    V1.0

    template <typename T>
    void __merge(T arr[], int L, int mid, int R)
    {
        T aux[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) {			//L....-->mid, now i has bigger than (L...mid)every one
                arr[k] = aux[j-L];
                j++;
            }
            else if(j > R) {	//mid....-->R, now j has bigger than (mid...R)every one
                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)
    {
        if(L >= R)
            return;
    
        int mid = (L+R)/2;	//递归的对半拆分
        __mergeSort(arr, L, mid-1);
        __mergeSort(arr, mid+1, R);
        __merge(arr, L, mid, R);	//回溯的比较与合并
    }
    
    template <typename T>
    void mergeSort(T arr[], int n)
    {
        __mergeSort(arr, 0, n-1);
    }
    

    此版本是第一版,可以看出来,有些地方是可以优化的,从前面的插入排序知道,在越有序,插入排序效率越高,一些场景下比nlogn时间复杂度的排序算法还高。因此,可以在最后基本有序的时候,使用插入算法来代替。

    同时,对于arr[mid] <= arr[mid+1],不进行merge操作的,直接返回就OK了,因为这种情况下,前面的那段肯定比后面的小(升序排序,降序逻辑相反)。

    优化后的:

    template <typename T>
    void __merge(T arr[], int L, int mid, int R)
    {
        T aux[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) {			//L....-->mid, now i has bigger than (L...mid)every one
                arr[k] = aux[j-L];
                j++;
            }
            else if(j > R) {	//mid....-->R, now j has bigger than (mid...R)every one
                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)
    {
        /*if(L >= R)
            return;*/
        //优化2:近乎有序时使用 插入排序 加快排序速度
        if(L >= R) {
            InsertSortFunc::InsertSort2(arr, L, R);
            return ;
        }
    
        int mid = (L+R)/2;
        __mergeSort(arr, L, mid-1);
        __mergeSort(arr, mid+1, R);
        //优化1:对arr[mid] <= arr[mid+1] 不进行merge
        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);
    }
    

    经过优化后,对于近乎有序的数组效率是非常高的,但是对于一般的情况下可能会有性能的消耗。因为上面多了一个函数调用,就有栈活动和函数切换返回的消耗。所以看情况使用。

    3.归并排序算法分析

    当数组长为N,将数列分开成最小单元需要logN步,每步都是一个合并有序数列的过程,时间复杂度可以记为O(N),所以共O(NlogN)。因为归并排序每次都是在相邻的数据中进行操作,所以归并排序在O(NlogN)的几种排序方法(快速排序,归并排序,希尔排序,堆排序)效率比较高的。

    4.归并排序多种场景下的测试

    int N = 20000;
    //稀疏数组,随机性大
    int *arr1 = SortTestHelper::CreateRandomArray(N, 0, 1000000);
    int *arr2 = SortTestHelper::copyIntArray(arr1, N);
    int *arr3 = SortTestHelper::copyIntArray(arr1, N);
    SortTestHelper::testSort("SelectionSort", SelectSortFunc::SelectionSort2, arr1, N);
    SortTestHelper::testSort("InsertSort", InsertSortFunc::InsertSort, arr2, N);
    SortTestHelper::testSort("mergeSort", mergeSortFunc::mergeSort, arr3, N);
    cout << endl;
    
    //近乎有序
    int *arr4 = SortTestHelper::CreateRandomArray(N, 0, 100);
    int *arr5 = SortTestHelper::copyIntArray(arr4, N);
    int *arr6 = SortTestHelper::copyIntArray(arr4, N);
    SortTestHelper::testSort("SelectionSort", SelectSortFunc::SelectionSort2, arr4, N);
    SortTestHelper::testSort("InsertSort", InsertSortFunc::InsertSort, arr5, N);
    SortTestHelper::testSort("mergeSort", mergeSortFunc::mergeSort, arr6, N);
    cout << endl;
    
    //基本有序
    int *arr7 = SortTestHelper::CreateRandomArray(N, 0, 10);
    int *arr8 = SortTestHelper::copyIntArray(arr7, N);
    int *arr9 = SortTestHelper::copyIntArray(arr7, N);
    SortTestHelper::testSort("SelectionSort", SelectSortFunc::SelectionSort2, arr7, N);
    SortTestHelper::testSort("InsertSort", InsertSortFunc::InsertSort, arr8, N);
    SortTestHelper::testSort("mergeSort", mergeSortFunc::mergeSort, arr9, N);
    cout << endl;
    
    delete[] arr1;delete[] arr2;delete[] arr3;delete[] arr4;
    delete[] arr5;delete[] arr6;delete[] arr7;delete[] arr8;delete[] arr9;
    
    测试结果:

    优化前:
    --------随机数组,随机性大
    SelectionSort : 0.412177s
    InsertSort : 0.251195s
    mergeSort : 0.002841s

    --------近乎有序
    SelectionSort : 0.41445s
    InsertSort : 0.256053s
    mergeSort : 0.002498s

    --------基本有序
    SelectionSort : 0.426011s
    InsertSort : 0.232978s
    mergeSort : 0.002227s


    优化后:
    --------随机数组,随机性大
    SelectionSort : 0.416574s
    InsertSort : 0.255791s
    mergeSort : 0.001487s

    --------近乎有序
    SelectionSort : 0.410324s
    InsertSort : 0.249639s
    mergeSort : 0.001583s

    --------基本有序
    SelectionSort : 0.417486s
    InsertSort : 0.23147s
    mergeSort : 0.001419s

    所以,优化后的归并排序总体上都比没优化的快,而且对越有序的数组,效率更高。但是插入排序不是一种原地性的排序算法,需要开额外的内存空间,这也算一个遗憾吧。

  • 相关阅读:
    10_SpringBoot集成TkMybatis插件
    Mysql-YUM安装
    docker数据拷贝
    jquery实现倒计时功能
    CentOS 7.2使用源码包编译安装MySQL 5.7.22及一些操作
    vue中开发webSocket
    YARN 与Maprd 配置
    js实现轮播图2
    DOM
    js五星好评
  • 原文地址:https://www.cnblogs.com/liangjf/p/8793791.html
Copyright © 2020-2023  润新知