云计算百科
云计算领域专业知识百科平台

12.归并排序:分治思想的稳定排序算法

一、什么是归并排序?

归并排序(Merge Sort)是一种基于分治思想的稳定排序算法,由 John von Neumann 在 1945 年提出。它的核心思想是先将数组递归拆分成单个元素,再将有序的子数组两两合并,最终得到完全有序的数组。

简单来说,归并排序就像 “拆积木再拼起来”:

  • 把复杂的大数组拆成一个个小数组,直到每个子数组只有一个元素(天然有序);
  • 再把这些有序的子数组两两合并,逐步拼成一个完整的有序数组。

二、归并排序的核心步骤

归并排序的核心步骤可以分为以下两步:

  • 拆分(Divide):将数组从中间分成两个子数组,递归拆分直到每个子数组只有一个元素;
  • 合并(Merge):将两个有序的子数组合并成一个更大的有序数组,重复这个过程直到所有子数组合并成一个完整的数组。
  • 三、归并排序的代码实现

    1. Python 版本(直观易懂)

    def merge_sort(arr, left, right):
    # 递归终止条件:子数组只有一个元素
    if left >= right:
    return
    # 1. 拆分:从中间分成两个子数组
    mid = (left + right) // 2
    merge_sort(arr, left, mid)
    merge_sort(arr, mid + 1, right)
    # 2. 合并:将两个有序子数组合并
    merge(arr, left, mid, right)

    def merge(arr, left, mid, right):
    # 定义左右子数组
    L = arr[left:mid+1]
    R = arr[mid+1:right+1]
    # 定义三个指针:i遍历左子数组,j遍历右子数组,k遍历原数组
    i = j = 0
    k = left
    # 比较左右子数组的元素,将较小的元素放入原数组
    while i < len(L) and j < len(R):
    if L[i] <= R[j]:
    arr[k] = L[i]
    i += 1
    else:
    arr[k] = R[j]
    j += 1
    k += 1
    # 处理左子数组剩余的元素
    while i < len(L):
    arr[k] = L[i]
    i += 1
    k += 1
    # 处理右子数组剩余的元素
    while j < len(R):
    arr[k] = R[j]
    j += 1
    k += 1

    # 测试
    arr = [12, 11, 13, 5, 6, 7]
    n = len(arr)
    merge_sort(arr, 0, n – 1)
    print("排序后的数组:", arr) # 输出:[5, 6, 7, 11, 12, 13]

    2. C 语言版本(更贴近底层)

    #include <stdio.h>
    #include <stdlib.h>

    // 合并两个有序子数组
    void merge(int arr[], int left, int mid, int right) {
    // 计算左右子数组的大小
    int n1 = mid – left + 1;
    int n2 = right – mid;
    // 分配临时数组
    int* L = (int*)malloc(n1 * sizeof(int));
    int* R = (int*)malloc(n2 * sizeof(int));
    // 复制数据到临时数组
    for (int i = 0; i < n1; i++) {
    L[i] = arr[left + i];
    }
    for (int j = 0; j < n2; j++) {
    R[j] = arr[mid + 1 + j];
    }
    // 定义三个指针:i遍历左子数组,j遍历右子数组,k遍历原数组
    int i = 0, j = 0, k = left;
    // 比较左右子数组的元素,将较小的元素放入原数组
    while (i < n1 && j < n2) {
    if (L[i] <= R[j]) {
    arr[k] = L[i];
    i++;
    } else {
    arr[k] = R[j];
    j++;
    }
    k++;
    }
    // 处理左子数组剩余的元素
    while (i < n1) {
    arr[k] = L[i];
    i++;
    k++;
    }
    // 处理右子数组剩余的元素
    while (j < n2) {
    arr[k] = R[j];
    j++;
    k++;
    }
    // 释放临时数组
    free(L);
    free(R);
    }

    // 归并排序函数
    void merge_sort(int arr[], int left, int right) {
    if (left < right) {
    // 1. 拆分:从中间分成两个子数组
    int mid = left + (right – left) / 2;
    merge_sort(arr, left, mid);
    merge_sort(arr, mid + 1, right);
    // 2. 合并:将两个有序子数组合并
    merge(arr, left, mid, right);
    }
    }

    // 打印数组
    void printArray(int arr[], int n) {
    for (int i = 0; i < n; i++) {
    printf("%d ", arr[i]);
    }
    printf("\\n");
    }

    int main() {
    int arr[] = {12, 11, 13, 5, 6, 7};
    int n = sizeof(arr) / sizeof(arr[0]);

    printf("排序前:");
    printArray(arr, n);

    merge_sort(arr, 0, n – 1);

    printf("排序后:");
    printArray(arr, n); // 输出:5 6 7 11 12 13

    return 0;
    }

    四、归并排序的时间复杂度和空间复杂度

    • 时间复杂度:O (n log n),因为每次拆分将数组分成两部分,递归深度为 log n,每层合并的时间复杂度为 O (n);
    • 空间复杂度:O (n),需要额外的临时数组存储子数组;
    • 稳定性:稳定,因为合并时会优先选择左子数组的元素,保持相同元素的相对位置。

    五、归并排序的优化

    为了提高归并排序的效率,可以进行以下优化:

  • 小数组优化:当子数组的大小小于某个阈值(如 10)时,使用插入排序代替归并排序,因为插入排序在小规模数据上效率更高;
  • 原地归并:使用原地归并算法,减少额外的内存空间使用,但会增加时间复杂度;
  • 迭代实现:使用迭代代替递归,减少递归调用栈的深度,避免栈溢出。
  • 六、归并排序的实际应用场景

    归并排序是一种稳定、高效的排序算法,常见场景包括:

  • 大规模数据排序:归并排序的时间复杂度稳定为 O (n log n),适合大规模数据排序;
  • 外部排序:当数据量太大无法全部加载到内存时,使用归并排序进行外部排序;
  • 稳定性要求高的场景:比如排序的元素是对象,需要保持相同元素的相对位置;
  • 链表排序:归并排序适合链表排序,因为链表的合并操作不需要额外的内存空间。
  • 七、总结

    归并排序是一种基于分治思想的稳定排序算法,它的核心思想是先将数组递归拆分成单个元素,再将有序的子数组两两合并,最终得到完全有序的数组。

    归并排序的时间复杂度为 O (n log n),空间复杂度为 O (n),是一种稳定的排序算法,适合大规模数据排序和稳定性要求高的场景。

    希望这篇文章能帮助你理解归并排序的原理和实现!

     

    赞(0)
    未经允许不得转载:网硕互联帮助中心 » 12.归并排序:分治思想的稳定排序算法
    分享到: 更多 (0)

    评论 抢沙发

    评论前必须登录!