归并排序的非递归实现

一、归并排序

归并排序是一种很好的排序算法:

①时间复杂度稳定为nlogn

②排序具有稳定性

但是传统的归并排序是用递归实现的,递归就会占用大量的栈空间,我们知道,在算法竞赛中对栈空间的要求十分严格,大部分评测机的栈空间限制只有2MB,稍微差一点的可能只有1MB。

因此为了节约栈空间,我们能不能在不牺牲归并排序性能的前提下,使用非递归的方法实现归并排序呢?

二、递归版归并排序的实现

递归实现的归并排序其实就是做了两件事:

“分解”:将序列每次折半划分(递归实现)

“归并”:将划分后的序列两两合并后排序

算法实现:

#include <iostream>

using namespace std;

const int N = 100010;

int n;
int a[N]; //待排序数组
int t[N]; //辅助空间

//归并
void Merge(int a[], int t[], int l, int mid, int r)
{
    int i = l, j = mid + 1, k = l; //三个指针
    while(i <= mid && j <= r)
    {
        if(a[i] <= a[j]) t[k ++] = a[i ++];
        else t[k ++] = a[j ++];
    }
    
    while(i <= mid) t[k ++] = a[i ++];
    while(j <= r) t[k ++] = a[j ++];
    for(int i = l;i <= r;i ++) a[i] = t[i];
}

//分解
void MergeSort(int a[], int t[], int l, int r)
{
    if(l == r) return ;
    int mid = l + r >> 1;
    MergeSort(a, t, l, mid), MergeSort(a, t, mid + 1, r); //递归分解,每次将序列分解为原来的一半
    
    Merge(a, t, l, mid, r);
}

int main ()
{
    scanf("%d", &n);
    for(int i = 0;i < n;i ++) scanf("%d", &a[i]);
    
    MergeSort(a, t, 0, n - 1);
    
    if(n) printf("%d", a[0]);
    for(int i = 1;i < n;i ++) printf(" %d", a[i]);
    
    return 0;
}

三、非递归版归并排序的实现

相比较于递归方法,非递归方法与之不同点就是:

在递归方法中,我们通过递归的方法递归到最底层,然后进行归并排序;而在非递归方法中,我们直接把数组分成两个数一组进行排序,然后再四个数一组进行排序,以此类推直到全部有序

举个栗子:

算法实现:

算法核心是两层循环:

第一层:循环有序序列的长度,当有序序列长度大于a[]长度,说明已经排序完成,break;

第二层:枚举a[]的开头到结尾,将长度为length的序列归并为长度为length * 2的有序列,完成归并

#include <iostream>
#include <math.h>

using namespace std;

const int N = 100010;

int a[N]; //待排序序列
int t[N]; //辅助空间

//归并a[l ~ r - 1]和a[r ~ n]
void merge(int a[], int t[], int l, int r, int n, int N)
{
    int i = l, j = r, k = l; //三个指针
    while(i <= r - 1 && j <= n && i < N && j < N)
        if(a[i] <= a[j]) t[k ++] = a[i ++];
        else t[k ++] = a[j ++];
    
    while(i <= r - 1 && i < N) t[k ++] = a[i ++];
    while(j <= n && j < N) t[k ++] = a[j ++];

    for(int i = l;i <= n && i < N;i ++) a[i] = t[i]; //将有序的t[]复制回a[]
}

void  MergeSort(int a[],  int N )
{
    int  length = 1; //长度为1的序列是有序的序列
    while( length < N ) //循环已经排好序的子序列长度
    { 
        int len = length * 2;
        //从序列开头到结尾,将长度为length的有序序列归并为长度为length * 2的序列
        for(int i = 0;i < N;i += len)
        {
            merge(a, t, i, i + length, i + len - 1, N);
        }
        length *= 2; //每次把已经排好序的序列长度乘2
    }
}

int main ()
{
    int n;
    scanf("%d", &n);
    for(int i = 0;i < n;i ++) scanf("%d", &a[i]);

    MergeSort(a, n);

    if(n) printf("%d", a[0]);
    for(int i = 1;i < n;i ++) printf(" %d", a[i]);

    return 0;
}

写在最后:

感谢您观看我的文章,如果您有更巧妙的思路和想法,或者发现本文章有些许错误的话,欢迎您在评论区批评指正~

  • 3
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值