合并排序

    合并排序算法是用分治策略实现对n个元素进行排序的算法。其基本思想是:将带排序元素分成大小大致相同的两个子集合,分别对这两个子集合进行排序,最终将排好序的子集合合并成所要求的排好序的集合。代码如下:

#include <stdio.h>
#include <stdlib.h>

void merge_sort(int[], int, int);
void merge(int[], int, int, int);

int main()
{
    int a[20],i=0;
    srand(5);
    while(i<20)    a[i++] = (int)rand();    i=0;
    printf("Before sort:\n");
    while(i<20)    printf("%d\n", a[i++]);  i=0;
    merge_sort(a, 0, 19);
    printf("\nAfter sort:\n");
    while(i<20)    printf("%d\n", a[i++]);
    return 0;
}

void merge_sort(int a[], int left, int right)
{
    int mid;
    if(left < right)
    {
        mid = (left+right)/2;       //取中点
        merge_sort(a, left, mid);   //对左边进行合并排序
        merge_sort(a, mid+1, right);//对右边进行合并排序
        merge(a, left, mid, right); //把左右两部分合并在一起
    }
}

void merge(int a[], int left, int mid, int right) //把a[left:mid]和a[mid+1:right]合并到a[left:right]
{
    int i=left, j=mid+1, k=0, b[20];
    while((i<=mid)&&(j<=right))                   //先把a[left:mid]和a[mid+1:right]合并到b[]中
        if(a[i]<=a[j])  b[k++] = a[i++];
        else            b[k++] = a[j++];
    if(i>mid)     while(j<=right) b[k++] = a[j++];
    else        while(i<=mid)   b[k++] = a[i++];
    i=left; k=0;                                  //把b[]复制到a[left:right]中
    while(i<=right)     a[i++] = b[k++];
}


    其中,算法merge_sort是用合并排序算法对a[ ]进行排序。merge算法合并两个排好序的数组到一个新的数组b[ ]中,然后再复制回数组a[ ]中。merge算法可以在O( n )时间内完成, 因此merge_sort算法最坏情况下所需的计算时间为O( nlogn )。

    对于算法merge_sort,还可以从多方面对它进行改进。比如,荣分支策略的机制入手,可以是消除算法中的递归。事实上,算法merge_sort的递归过程只是讲待排序集合一分为二,直至待排序集合只剩下一个元素为止,然后不断合并两个排好序的数组段。按照这种方法,可以先将数组a中相邻元素两两配对。用合并算法将它们排序,构成n/2组长度为2的排好序的子数组段,然后再将它们排序乘长度为4的排好序的子数组段,如此继续下去,直到整个数组排好序。消去递归后的合并算法如下,参数表有所变化:

void merge_sort(int a[], int length)          //length为数组a[]的长度
{
    int i=1,j;
    while(i < length)
    {
        j = 0;
        while(j+2*i-1 < length)
        {
            merge(a, j, j+i-1, j+2*i-1); //合并大小为i的相邻2段数组
            j = j+2*i;
        }
        if(j+i-2 < length)               //剩下的元素个数少于2s的情况
            merge(a, j, j+i-1, length-1);
        i = i*2;
    }
}

自然合并排序是merge_sort的一个变形。对于初步给定的数组a[ ],通常存在长度大于1的已自然排好序的子数组段。用1次对数组a[ ]的线性扫描就可以找出所有已排好序的子数组段。再将这些子数组段两两合并,构成更大的子数组段。在通常情况下,按此方式进行合并排序所需的合并次数较少。例如,{4, 8, 3, 7, 1, 5, 6, 2}中,自然排好序的子数组段为{4, 8} {3, 7} {1, 5, 6} {2}。经过一次合并可得到{3, 4, 7, 8},和{1, 2, 5, 6},再合并就得到{1, 2, 3, 4, 5, 6, 7, 8}。算法如下,参数表有所变化:

void merge_sort(int a[])
{
    int i=0, j=1, k=1, b[LENGTH];//LENGTH是个常量,表示数组a[]的长度
    b[0]=-1;                     //b[0]=-1是为了能正确使用第二个while循环中的merge函数
    while(i+1<LENGTH)            //数组b[]中储存了数组a[]中自然排好序的部分之间断开的位置
    {
        if(a[i]>a[i+1])
            b[j++]=i;
        i++;
    }
    b[j]=LENGTH-1;
    while(2*k < LENGTH)          //根据b[]合并排序
    {
        i=0;
        while(i+2*k <= j)
        {
            merge(a, b[i]+1, b[i+k], b[i+2*k]);
            i=i+2*k;
        }
        if(i+k<j)
            merge(a, b[i]+1, b[i+k], b[j]);
        k=k*2;
    }
}


运行结果:

Before sort:
590011675
99788765
2131925610
171864072
317159276
171035632
602511920
963050649
1069979073
1919854381
33661026
589806848
2086105860
475191198
894416410
550050020
780437020
583787226
893281828
550277486


After sort:
33661026
99788765
171035632
171864072
317159276
475191198
550050020
550277486
583787226
589806848
590011675
602511920
780437020
893281828
894416410
963050649
1069979073
1919854381
2086105860
2131925610

参考文献:王晓东, 计算机算法设计与分析(第3版),电子工业出版社,北京,2007

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值