排序思想
① 初始时,将每个记录看成一个单独的有序序列,则n个待排序记录就是n个长度为1的有序子序列;
② 对所有有序子序列进行两两归并,得到┏n/2┓个长度为2或1的有序子序列——一趟归并;
③ 重复② ,直到得到长度为n的有序序列为止。
上述排序过程中,子序列总是两两归并,称为2-路归并排序。其核心是如何将相邻的两个子序列归并成一个子序列。设相邻的两个子序列分别为:
{R[k], R[k+1], …, R[m]}和{R[m+1], R[m+2],…, R[h]},将它们归并为一个有序的子序列:
{DR[l], DR[l+1], …, DR[m], DR[m+1], …, DR[h] }
排序示例
设有9个待排序的记录,关键字分别为23, 38, 22, 45, 23, 67, 31, 15, 41,归并排序的过程如图1所示。
算法实现
1. 非递归方式
/* 二路归并算法 */
/* 将有序的R[low..mid]和X[mid+1..high]归并为有序的DR[low..high] */
void Merge(int R[], int DR[], int low, int mid, int high)
{
int i, j, k;
i = k = low;
j = mid+1;
while(i<=mid && j<=high)
{
if(R[i] <= R[j]) /* 比较两个子序列 */
DR[k++] = R[i++];
else
DR[k++] = R[j++];
}
while(i<=mid) /* 将R中剩余元素R[i...mid]复制到DR */
DR[k++] = R[i++];
while(j<=high) /* 将R中剩余元素R[j...high]复制到DR */
DR[k++] = R[j++];
}
/* 一趟归并排序 */
/* 一趟归并排序都是从前到后,依次将相邻的两个有序子序列归并为一个,
/* 且除最后一个子序列外,其余每个子序列的长度都相同。 */
/* 设这些子序列的长度为d,则一趟归并排序的过程是: */
/* 从j=0开始,依次将相邻的两个有序子序列R[j…j+d-1]和R[j+d…j+2d-1]进行归并;
/* 每次归并两个子序列后,j后移动2d个位置,即j=j+2d; */
/* 若剩下的元素不足两个子序列时,分以下两种情况处理: */
/* ① 剩下的元素个数>d:再调用一次上述过程,将一个长度为d的子序列和不足d的子序列进行归并; */
/* ② 剩下的元素个数≤d:将剩下的元素依次复制到归并后的序列中。 */
void Merge_pass(int R[], int DR[], int d, int n)
{
int j=0;
while ((j+2*d-1)<n)
{
Merge(R, DR, j, j+d-1, j+2*d-1);
j=j+2*d;
} /* 子序列两两归并 */
if(j+d-1<n) /* 剩余元素个数超过一个子序列长度d */
Merge(R, DR, j, j+d-1, n);
else
Merge(R, DR, j, n, n); /* 剩余子序列复制 */
}
/* 开始归并时,每个记录是长度为1的有序子序列,对这些有序子序列逐趟归并,每一趟归并后有序子序列的长度均扩大一倍; */
/* 当有序子序列的长度与整个记录序列长度相等时,整个记录序列就成为有序序列。算法是: */
BOOL Merge_sort(int R[], int DR[], int length)
{
int d=1;
while(d<length)
{
Merge_pass(R, DR, d, length);
Merge_pass(DR, R, 2*d, length);
d=4*d;
}
return true;
}
2. 递归方式
//将有二个有序数列R[low...mid]和R[mid+1...high]合并。
void Merge(int R[], int low, int mid, int high, int temp[])
{
int i, j, k;
i = k = low;
j = mid + 1;
while (i <= mid && j <= low)
{
if (R[i] <= R[j])
temp[k++] = R[i++];
else
temp[k++] = R[j++];
}
while (i <= mid)
temp[k++] = R[i++];
while (j <= high)
temp[k++] = R[j++];
for (i = 0; i < k; i++)
R[low + i] = temp[i];
}
void mergeSort(int R[], int low, int high, int temp[])
{
if (low < high)
{
int mid = (low + high) / 2;
mergeSort(a, low, mid, temp); //左边有序
mergeSort(a, mid + 1, high, temp); //右边有序
Merge(a, low, mid, high, temp); //再将二个有序数列合并
}
}
bool MergeSort(int R[], int n)
{
int DR[n] = {0};
mergeSort(R, 0, n - 1, DR);
return true;
}
算法分析
具有n个待排序记录的归并次数是㏒2n,而一趟归并的时间复杂度为O(n),则整个归并排序的时间复杂度无论是最好还是最坏情况均为O(n㏒2n)。在排序过程中,使用了辅助向量DR,大小与待排序记录空间相同,则空间复杂度为O(n)。归并排序是稳定的。