算法思想
合并排序算法是用分治策略实现对n个元素进行排序的算法。其基本思想是:将待排序元素分成大小大致相同的两个子集合,分别对两个子集合进行排序,最终将排好序的子集合合并成所要求的排好序的集合。合并算法可递归描述如下:
void MergeSort(int a[], int left, int right)
{
if (left < right)
{ //至少还有两个元素
int mid = (left + right) / 2; //取中点
MergeSort(a, left, mid);
MergeSort(a, mid+1, right);
Merge(a, b, left, mid, right); //合并到数组b
Copy(a, b, left, right); //复制回数组a
}
}
其中,算法Merge合并两个排好序的数组段到一个新的数组b中,然后由Copy将合并后的数组段再复制回数组a中。Merge和Copy都可在O(n)时间内完成,因此合并排序算法对n个元素进行排序,在最坏情况下所需的计算时间T(n)满足:
T(n)={O(1)2T(n/2)+O(n)n <= 1n > 1
解此递归方程可得T(n) = O(nlgn).由于排序问题的计算时间下界为
Ω(nlgn) ,所以合并排序算法是一个渐进最优算法。
实现代码
#include <stdio.h>
#include <stdlib.h>
void MergeSort(int a[], int left, int right);
void Merge(int a[], int b[], int l, int m, int r);
void Copy(int a[], int b[], int s, int e);
int main()
{
int i, n;
printf("please enter array size:");
scanf("%d", &n);
int a[n];
printf("please enter number:");
for (i = 0; i < n; i++)
scanf("%d", &a[i]);
printf("array:\n");
for (i = 0; i < n; ++i)
printf("%4d", a[i]);
printf("\n");
MergeSort(a, 0, n-1);
printf("sort array:\n");
for (i = 0; i < n; ++i)
printf("%4d", a[i]);
printf("\n");
return 0;
}
void MergeSort(int a[], int left, int right)
{ //合并排序
if (left < right)
{ //至少还有两个元素
int n = right - left + 1; //b数组大小
int b[n]; //下标从0开始
int mid = (left + right) / 2; //取中点
MergeSort(a, left, mid); //排序左边部分
MergeSort(a, mid + 1, right); //排序右边部分
Merge(a, b, left, mid, right); //合并到数组b
Copy(a, b, left, right); //复制回数组a
}
}
void Merge(int a[], int b[], int l, int m, int r)
{ //把a数组排好序的左右两部分合并到b数组中
int i, j, k;
i = l; //i指向a左边起始下标
j = m + 1; //j指向a右边起始下标
k = 0; //k指向b数组起始下标
while (i <= m && j <= r)
{
if (a[i] < a[j])
b[k++] = a[i++];
else
b[k++] = a[j++];
}
/*把数组剩余的部分复制到b中*/
while (i <= m)
b[k++] = a[i++];
while (j <= r)
b[k++] = a[j++];
}
void Copy(int a[], int b[], int s, int e)
{ //b数组复制到a数组
int i, j;
for (i = s, j= 0; i <= e; ++i, ++j)
a[i] = b[j];
}
算法改进
对于算法MergeSort,还可以从分支策略的机制入手,容易消除算法中的递归。事实上,算法MergeSort的递归过程只是将待排序集合一分为二,直至待排序集合只剩下一个元素为止,然后不断合并两个排好序的数组段。按此机制,可以首先将数组a中相邻元素两两配对。用合并算法将他们排序,构成n/2组长度为2的排好序的子数组段,然后将他们排序成长度为4的排好序的子数组段,如此继续下去,直至整个数组排好序。
按此思想,消去递归后的合并排序算法可描述如下:
void MergeSort(int a[], int n)
{
int b[n];
int s = 1;
while (s < n)
{
MergeSort(a, b, s, n); //合并到数组b
s += s;
MergeSort(b, a, s, n); //合并到数组a
s += s;
}
}
其中MergePass用于合并排好序的相邻数组段。具体的合并算法由Merge来实现。对于MergePass函数,合并排好序的相邻数组段。到最后,可能剩下元素不足2s个,对于大于s个元素和小于等于s个元素分别采用不同的处理方式。具体见函数定义。
对于函数Merge,与之前大致相同,只是数组b下标从l开始,而之前的Merge函数,下标从0开始。
改进后的代码
/* **合并排序——非递归版本 */
#include <stdio.h>
#include <stdlib.h>
void MergeSort(int a[], int n);
void MergePass(int a[], int b[], int s, int n);
void Merge(int a[], int b[], int l, int m, int r);
int main()
{
int i, n;
printf("please enter array size:");
scanf("%d", &n);
int a[n];
printf("please enter number:");
for (i = 0; i < n; i++)
scanf("%d", &a[i]);
printf("array:\n");
for (i = 0; i < n; ++i)
printf("%4d", a[i]);
printf("\n");
MergeSort(a, n);
printf("sort array:\n");
for (i = 0; i < n; ++i)
printf("%4d", a[i]);
printf("\n");
return 0;
}
void MergeSort(int a[], int n)
{ //合并排序
int b[n];
int s = 1;
while (s < n)
{
MergePass(a, b, s, n); //合并到数组b
s += s;
MergePass(b, a, s, n); //合并到数组a
s += s;
}
}
void MergePass(int a[], int b[], int s, int n)
{
int i = 0, j;
while (i <= n - 2 * s)
{ //max(i) 到 n-1的闭区间大小为2s
Merge(a, b, i, i+s-1, i+2*s-1); //合并大小为s的相邻2段子数组
i = i + 2*s;
}
//剩下的运算个数少于2s个
if (i + s < n)
{ //至少还有s+1个元素
Merge(a, b, i, i+s-1, n-1);
}
else
{ //最多剩下s个元素
for (j = i; j < n; j++)
b[j] = a[j];
}
}
void Merge(int a[], int b[], int l, int m, int r)
{ //合并a[l:m]和a[m+1:r]到b[l:r]
int i = l, j = m+1, k = l;
while (i <= m && j <= r)
{
if (a[i] <= a[j])
b[k++] = a[i++];
else
b[k++] = a[j++];
}
/*把数组剩余的部分复制到b中*/
while (i <= m)
b[k++] = a[i++];
while (j <= r)
b[k++] = a[j++];
}