19. 排序--歸併排序
阿新 • • 發佈:2019-01-01
歸併排序
將已有序的子序列合併,得到有完全有序的序列
核心:有序子列的歸併
// 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);
}