排序——归并排序

排序——归并排序

1. 思想

​ 归并排序的思想是:如果要对一个数组进行排序,可以先(递归地)将它分成两半分别排序,然后将结果归并起来。归并算法最吸引人的性质是它能保证将任意长度为N的数组排序所需时间和 N l o g N NlogN NlogN 成正比;缺点则是它所需的额外空间和N成正比。

2. 原地归并的抽象方法

​ 将两个不同的有序数组归并起来,可以采用原地归并的方法,这样就可以先将前半部分排序,再将后半部分排序,然后在数组中移动元素而不需要使用额外的空间。

​ 下面就是对原地归并的抽象方法:

// 该方法可以将子数组 a[l...mid] 和 a[mid+1...r]归并成一个有序数组,并存放在a[l...r]
// 子数组 a[l...mid] 和 a[mid+1...r]都是有序的
void merge(int *a, int l, int mid, int r) {
        int *aux = new int(r+1); // aux是辅助空间,大小至少为r+1
        int i, j, k;

        for(i=l; i<=r; i++) {  // 将a[l...r]复制到aux[l...r]
            aux[i] = a[i];
        }

        i = l;
        j = mid + 1;
        for(k=l; k<=r; k++) {  // 归并回到a[l...r]
            if(i > mid){ // 左半边用尽(取右半边的元素)
                a[k] = aux[j++];
            }
            else if(j > r){ // 右半边用尽(取左半边的元素)
                a[k] = aux[i++];
            }
            else if(aux[i] > aux[j]) { //右半边当前元素小于左半边当前元素(取右半边元素)
                a[k] = aux[j++];
            }
            else { //右半边当前元素大于左半边当前元素(取左半边元素)
                a[k] = aux[i++];
            }
        }
        delete[] aux;
    }

3. 自顶向下的归并排序(递归)

【思想】

​ 自顶向下的归并排序应用了分治的思想。

【算法过程】

​ 如果要对子数组 a [ l . . . r ] a[l...r] a[l...r] 进行排序,自顶向下的归并排序会先将 a [ l . . . r ] a[l...r] a[l...r] 分为 a [ l . . . m i d ] a[l...mid] a[l...mid] a [ l . . . r ] a[l...r] a[l...r] 两部分,分别通过递归调用将它们单独排序,最后将有序的子数组归并为最终的排序结果。

【代码】
// 对数组a[l..r]进行自顶向下的归并排序
void top_down_sort(int *a, int l, int r) {
        if(l >= r) return;
        int mid = l + (r - l) / 2;
        top_down_sort(a, l, mid);  // 将左半边排序
        top_down_sort(a, mid+1, r); // 将右半边排序
        merge(a, l, mid, r);   // 归并结果
    }
【算法分析】

​ 对于长度为N的任意数组,自定向下的归并排序:

(1)时间复杂度为 N l o g N NlogN NlogN

(2)空间复杂度为N;

(3)是稳定排序;

(4)不是原地排序。

4. 自底向上的归并排序(非递归)

【思想】

​ 自底向上的归并排序是一种非递归的方法,它的思想是:先归并那些微型数组,然后再成对归并得到的子数组,如此这般,知道将整个数组归并到一起

【算法过程】

​ 自底向上的归并排序,首先进行的是两两归并(把每个元素想象成一个大小为1的数组),然后四四归并(将两个大小为2的数组归并成一个4个元素的数组),然后八八归并,一直下去。例如:

对数组[5, 4, 9, 2, 6]进行自底向上的归并排序:

第一轮(两两归并):
(5, 4) -> (4, 5)   (9, 2) -> (2, 9)  

第二轮(四四归并)
(4, 5)(2, 9) -> (2, 4, 5, 9)

第三轮(八八归并)
(2, 4, 5, 9)(6) -> (2, 4, 5, 6, 9)

在每一轮归并中,最后一次归并的第二个子数组可能比第一个数组要小(例如上例中的第三轮,但这对merge()方法不是问题),否则两个数组应该是大小一样的,而这在下一轮中子数组的大小会翻倍。

【代码】
// 对长度为n的数组a进行自底向上的归并排序
void down_top_sort(int *a, int n) {
        int sz, lo;

    	// 共进行lgN次两两归并
        for(sz=1; sz<n; sz+=sz) { // sz是子数组的长度
            for(lo=0; lo+sz<n; lo+=sz+sz) { // lo是子数组的索引
                merge(a, lo, lo+sz-1, min(n-1, lo+sz+sz-1));
            }
        }
    }

以下例子模拟上述代码过程:

对数组[5, 4, 9, 2, 6]进行自底向上的归并排序:

第一轮(两两归并,sz = 1):
merge(a, 0, 0, 1)  merge(a, 2, 2, 3)

第二轮(四四归并,sz = 2)
merge(a, 0, 1, 3)

第三轮(八八归并,sz = 4)
merge(a, 0, 3, 4)
【算法分析】

​ 对于长度为N的任意数组,自定向下的归并排序:

(1)时间复杂度为 N l o g N NlogN NlogN

(2)空间复杂度为N;

(3)是稳定排序;

(4)不是原地排序。

5. 综合起来

​ 将归并排序综合起来写成一个类,其代码及测试代码如下:

#include <iostream>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <algorithm>
#include <vector>

using namespace std;

class MergeSort {
private:
    int *aux;

public:
    // 自上向底
    void top_down_sort(int *a, int l, int r) {
        if(l >= r) return;
        int mid = l + (r - l) / 2;
        top_down_sort(a, l, mid);
        top_down_sort(a, mid+1, r);
        merge(a, l, mid, r);
    }

    // 自底向上
    void down_top_sort(int *a, int n) {
        int sz, lo;

        for(sz=1; sz<n; sz+=sz) {
            for(lo=0; lo+sz<n; lo+=sz+sz) {
                merge(a, lo, lo+sz-1, min(n-1, lo+sz+sz-1));
            }
        }
    }

    void merge(int *a, int l, int mid, int r) {
        aux = new int(r+1);
        int i, j, k;

        for(i=l; i<=r; i++) {
            aux[i] = a[i];
        }

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


};



int main()
{
    int a[18] = {16, 7, 13, 2, 8, 4, 3, 54};
    int i, j, k;

    MergeSort ms;
    ms.down_top_sort(a, 8);
    for(i=0; i<8; i++) {
        cout << a[i] << " " ;
    }
    cout << endl;

    return 0;
}

6. 算法分析

​ 归并排序对于长度为N的任意数组:

(1)时间复杂度为 N l o g N NlogN NlogN

(2)空间复杂度为N;

(3)是稳定排序;

(4)不是原地排序。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值