1. 程式人生 > >19. 排序--歸併排序

19. 排序--歸併排序

歸併排序

將已有序的子序列合併,得到有完全有序的序列

核心:有序子列的歸併

// 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)

歸併演算法實現

遞迴演算法

分而治之

  1. 遞迴的把序列一分為二,直到序列只剩2個元素(可能有一組只有一個元素)
  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);
    }    
}

時間複雜度:T(N)=O(NlogN)

統一函式介面

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);
}

非遞迴演算法

一趟歸併

  1. 對相鄰的兩段有序子列進行合併
  2. 兩兩子列進行合併,同一段子列不會在一趟歸併中參與兩次合併
  3. 需要額外處理剩餘的單個序列,保證最後把結果歸併到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);
}