归并排序
将已有序的子序列合并,得到有完全有序的序列
核心:有序子列的归并
// LeftStart=左边数组的起始位置,RightStart=右边数组的起始位置,RightEnd=右边数组的结束位置
void Merge(ElementType[] A, ElementType[] TmpA, int LeftStart, int RightStart, int RightEnd) {
LeftEnd = RightStart - 1; // 左边数组结束位置,假设左右数组紧挨
TmpIndex = LeftStart; // 存放结果数组的初始位置
LeftIndex = LeftStart;
RightIndex = RightStart;
while (LeftIndex <= LeftEnd && RightIndex <= RightEnd) {
if (A[LeftIndex] <= A[RightIndex])
TmpA[TmpIndex++] = A[LeftIndex++];
else
TmpA[TmpIndex++] = A[RightIndex++];
}
while (LeftIndex <= LeftEnd) // 直接复制左边剩下的
TmpA[TmpIndex++] = A[LeftIndex++];
while (RightIndex <= RightEnd) // 直接复制右边剩下的
TmpA[TmpIndex++] = A[RightIndex++];
for (i = LeftStart; i <= RightEnd; i++) // 将TmpA中的数据恢复到A中,如果在循环算法中,不需要执行这个恢复过程
A[i] = TmpA[i];
}
- 如果两个子列一共有N个元素,则归并的时间复杂度是
T(N)=O(N) - 注意:在归并过程中仅仅使用了一个临时数组,而不是每次都申请开辟一个临时数组,这样使额外的空间复杂度为
O(N)
归并算法实现
递归算法
分而治之
- 递归的把序列一分为二,直到序列只剩2个元素(可能有一组只有一个元素)
- 进行子序列的合并
void MSort(ElementType[] A, ElementType[] TmpA, int LeftStart, int RightEnd) {
int Center; // 中心位置的下标
if (LeftStart < RightEnd) { // 至少要有两个元素才用归并的意义
Center = (LeftStart + RightEnd) / 2;
MSort(A, TmpA, LeftStart, Center);
MSort(A, TmpA, Center + 1, RightEnd);
Merge(A, TmpA, LeftStart, Center + 1, RightEnd);
}
}
时间复杂度:
统一函数接口
void Merge_Sort(ElementType[] A, int N) {
ElmentType *TmpA;
TmpA = malloc(N * sizeof(ElementType));
if (TmpA == NULL) {
Error("空间不足");
return;
}
MSort(A, TmpA, 0, N - 1);
free(TmpA);
}
非递归算法
一趟归并
- 对相邻的两段有序子列进行合并
- 两两子列进行合并,同一段子列不会在一趟归并中参与两次合并
- 需要额外处理剩余的单个序列,保证最后把结果归并到TmpA中
// SubLen = 当前有序子列的长度
void Merge_Pass(ElementType[] A, ElementType[] TmpA, int N, int SubLen) {
for (i = 0; i <= N - 2 * SubLen; i += 2 * SubLen) // i <= N - 2 * SubLen保证循环过程中至少有一对子列可以进行合并
Merge(A, TmpA, i, i + SubLen, i + 2 * SubLen - 1); // Merge函数是将最后结果保存在TmpaA中
if (i + SubLen < N) // 说明最后还剩两个序列
Merge(A, TmpA, i, i + SubLen, N - 1);
else // 最后只剩一个序列
for (j = i; j < N; j++)
Tmp[j] = A[j]
}
统一函数接口
void Merge_Sort(ElementType[] A, int N) {
ElementType *TmpA;
TmpA = malloc(N * sizeof(ElementType));
if (TmpA == NULL) {
Error("空间不足");
return;
}
SubLen = 1;
while (SubLen < N) {
Merge_Pass(A, TmpA, N, SubLen);
SubLen *= 2; // 如果SubLen在乘2操作以后大于等于N了,则Merge_Pass只会做将TmpA的内容复制给A
Merge_Pass(TmpA, A, N, SubLen);
SubLen *= 2;
}
free(TmpA);
}