排序算法之归并排序

排序算法之归并排序

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

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

转载于:https://www.cnblogs.com/liangjf/p/8793791.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值