归并排序(Merge Sort)的递归与非递归实现

归并排序是一种稳定的分治算法,通过递归或非递归方式实现。递归实现中,将序列不断划分并合并;非递归实现则从长度为1的序列开始逐步合并。自然归并排序能利用已排序子序列加速。其时间复杂度为O(nlogn)。
摘要由CSDN通过智能技术生成

 

原理

归并排序采用分治的策略,是一个稳定排序算法。其基本思想是:将待排序的序列划分为长度大致相同的两个子序列,对两个子序列分别进行归并排序,即对子序列进行相同规则的划分与排序,直到子序列的长度为1,最终将排好序的子序列合并成排好序的序列。

实现

归并排序具体有两种实现方式:递归的与非递归的,亦称为从上往下的与从下往上的。

递归实现

思路:

1)将长度为n的待排序序列arr[0:n](左闭右开)一分为二,即取序列中点mid = left + (right-left)/2,将序列分为arr[0:mid]与arr[mid:n]两个子序列

2)对两个子序列执行1)中的操作,直到分出的子序列长度为1

3)将划分的两个子序列合并,即得到排好序的序列

  • 动态分配临时数组空间
void Merge(int arr[], int left, int mid, int right)
{
    int i = left, j = mid, k = 0;
    int *tempArr = new int[right-left+1];
    while(i < mid || j < right)
    {
        if(j >= right || (i < mid && arr[i] <= arr[j]))
            tempArr[k++] = arr[i++];
        else
            tempArr[k++] = arr[j++];
    }
    for(k = left; k < right; ++k)
        arr[k] = tempArr[k-left];
    delete tempArr;
}

void MergeSort(int arr[], int left, int right)
{
    if(left+1 >= right) return;

    int mid = left + (right-left)/2;
    MergeSort(arr, left, mid);
    MergeSort(arr, mid, right);
    Merge(arr, left, mid, right);
}
  • 静态分配临时数组空间
void _merge(int* a, int* t, int left, int mid, int right) {
    int x = left, y = mid, z = left;
    while (x < mid || y < right) {
        if (y >= right || (x < mid && a[x] <= a[y])) {
            t[z++] = a[x++];
        } else {
            t[z++] = a[y++];
        }
    }
    for (int i = left; i < right; ++i) {
        a[i] = t[i];
    }
}

void _mergeSort(int* a, int* t, int left, int right) {
    if (left >= right-1) {
        return;
    }

    int mid = left + ((right-left)>>1);
    _mergeSort(a, t, left, mid);
    _mergeSort(a, t, mid, right);
    _merge(a, t, left, mid, right);
}

void mergeSort(int* a, int left, int right) {
    int t[right-left];
    _mergeSort(a, t, left, right);
}

非递归实现

非递归实现与递归实现相反,从长度为1的子序列开始排序,将它们两两合并,然后依次对长度为2、2^2、2^3...的序列进行排序与合并,直到完成所有数的排序。

而非递归实现中,有另一种做法,称为自然归并排序。事实上,对于给定的数组,通常存在多个长度大于1的已自然排好序的子序列段,如{2, 5, 7, 3, 1, 9, 4}中,{2, 5, 7}, {3}, {1, 9}, {4}就是四个已自然排好序的子序列段,可通过O(n)的遍历找出,将相邻的已自然排好序的子序列段两两合并,初次合并后继续两两合并已合并过一次的子序列段,直到整个数组都排好序。

这里实现的是一般的非递归实现

#include <bits/stdc++.h>
#define ll long long
#define INF 0x3f3f3f3f
using namespace std;

void Merge(int arr[], int left, int mid, int right)
{
    int i = left, j = mid, k = 0;
    int *tempArr = new int[right-left+1];
    while(i < mid || j < right)
    {
        if(j >= right || (i < mid && arr[i] <= arr[j]))
            tempArr[k++] = arr[i++];
        else
            tempArr[k++] = arr[j++];
    }
    for(k = left; k < right; ++k)
        arr[k] = tempArr[k-left];
    delete tempArr;
}

void MergeHelp(int arr[], int step, int len)
{
    int i = 0;
    while(i <= len-(step<<1))
    {
        Merge(arr, i, i+step, i+(step<<1)); //对arr[i:i+step],arr[i+step,i+2*step]排序
        i += (step<<1); //对下一对子序列进行合并
    }
    if(i+step < len) //如果剩下的数比step还多,就再进行一次Merge
        Merge(arr, i, i+step, len);
}

void MergeSort(int arr[], int len)
{
    int step = 1;
    while(step < len)
    {
        MergeHelp(arr, step, len);
        step <<= 1; //每次增大序列的长度
    }
}

int main()
{
    int n = 7, a[] = {2, 5, 7, 3, 1, 9, 4};
    MergeSort(a, n);
    for(int i = 0; i < n; ++i)
        cout << a[i] << ' ';
    return 0;
}

复杂度分析

归并排序每次将数据规模减半,且每次除去递归所需要做的操作的复杂度显然为O(n),那么有

T(n) = \left\{\begin{matrix} O(1)&n\leq 1 \\ 2T(n/2)+O(n) & n> 1 \end{matrix}\right.

根据主定理,有T(n) = Ω(nlogn)。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值