排序思想
① 初始时,将每个记录看成一个单独的有序序列,则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)。归并排序是稳定的。