合并排序算法是用分治策略实现对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