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);
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章