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

版权声明:嘤嘤嘤 https://blog.csdn.net/HNUCSEE_LJK/article/details/88360827

原理

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

实现

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

递归实现

思路:

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

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

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

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

//Merge的原理是,对比两个序列的第一个元素的大小,将小的那一个移入临时数组tempArr中,
//然后继续比对,直到两个序列的元素全都移入tempArr中,
//最后将tempArr中排好序的元素放回原数组中的相应段
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])) //还要再检查依次i < mid的原因是防止出现i越界
            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); //将划分的两个子序列合并
}
int main()
{
    int n = 7, a[] = {2, 5, 7, 3, 1, 9, 4};
    MergeSort(a, 0, n);
    for(int i = 0; i < n; ++i)
        cout << a[i] << ' ';
    return 0;
}

非递归实现

非递归实现与递归实现相反,从长度为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)。

猜你喜欢

转载自blog.csdn.net/HNUCSEE_LJK/article/details/88360827