Python 归并排序(详细教程)

Python 归并排序:理解分治思想的高效排序算法

在算法学习的旅程中,排序是绕不开的基础模块。当你面对一个无序的数据集合,如何高效地将其按从小到大或从大到小排列?快速排序、冒泡排序、插入排序……各有优劣。而今天我们要深入探讨的,是兼具稳定性和高效性的经典排序算法——归并排序。

它不仅是面试高频考点,更是理解“分治思想”的绝佳范例。尤其在 Python 语言环境下,其简洁的语法和递归支持,让归并排序的实现变得格外优雅。接下来,我将带你从零开始,亲手实现一个完整的 Python 归并排序,并理解它背后的逻辑与优势。


什么是归并排序?核心思想解析

归并排序(Merge Sort)是一种基于“分而治之”(Divide and Conquer)策略的排序算法。它的核心思想可以用一个生活中的比喻来理解:

想象你有一堆散乱的扑克牌,想把它们按花色和点数排好。你会怎么做?不会一根一根地比较,而是先将牌分成两堆,再分别把每堆排好,最后把两堆有序的牌合并成一堆。

这个过程,就是归并排序的精髓:分解 → 排序 → 合并

具体来说,归并排序的执行步骤如下:

  1. 将待排序数组从中间一分为二,得到左右两个子数组;
  2. 递归地对左右两个子数组进行归并排序;
  3. 将两个已排序的子数组合并成一个完整的有序数组。

这个过程不断递归,直到子数组长度为 1(单个元素自然是有序的),然后逐层向上合并,最终得到整个数组的有序结果。


分而治之:递归实现的逻辑骨架

Python 的递归语法天然适合实现归并排序。我们先来看一个核心函数的框架:

def merge_sort(arr):
    # 基础情况:如果数组长度小于等于 1,则无需排序,直接返回
    if len(arr) <= 1:
        return arr

    # 找到中点,将数组分为左右两部分
    mid = len(arr) // 2
    left_half = arr[:mid]      # 左半部分
    right_half = arr[mid:]     # 右半部分

    # 递归排序左右两部分
    left_sorted = merge_sort(left_half)
    right_sorted = merge_sort(right_half)

    # 合并两个已排序的数组
    return merge(left_sorted, right_sorted)

这段代码虽然简短,但包含了归并排序的全部逻辑。我们来逐行拆解:

  • if len(arr) <= 1: 是递归的终止条件。当数组只剩一个元素或为空时,它本身就是有序的,无需进一步操作。
  • mid = len(arr) // 2 使用整除获取中间位置,确保左右两部分尽可能平均。
  • arr[:mid]arr[mid:] 是 Python 的切片语法,分别提取左半和右半部分。
  • 递归调用 merge_sort 对左右两部分进行排序,这一步是“分而治之”的体现。
  • 最后调用 merge 函数合并两个有序数组,完成整个排序过程。

合并过程详解:如何将两个有序数组合并?

合并是归并排序中最关键的一步。它不复杂,但需要严谨的逻辑。我们来实现 merge 函数:

def merge(left, right):
    # 创建一个空列表用于存放合并结果
    result = []
    i = 0  # 左数组的指针
    j = 0  # 右数组的指针

    # 比较两个数组的当前元素,将较小者加入结果
    while i < len(left) and j < len(right):
        if left[i] <= right[j]:
            result.append(left[i])
            i += 1
        else:
            result.append(right[j])
            j += 1

    # 将左数组剩余元素全部加入结果(如果存在)
    while i < len(left):
        result.append(left[i])
        i += 1

    # 将右数组剩余元素全部加入结果(如果存在)
    while j < len(right):
        result.append(right[j])
        j += 1

    return result

这个函数的逻辑非常清晰:

  • 使用两个指针 ij 分别指向左右两个数组的起始位置;
  • 每次比较 left[i]right[j],将较小的元素加入 result,并移动对应指针;
  • 当其中一个数组遍历完后,另一个数组中剩余的元素一定是更大的,直接追加到结果末尾;
  • 最终返回合并后的有序数组。

💡 小贴士:这里使用 <= 而不是 < 是为了保证算法的稳定性——相等元素的相对顺序不会改变。这是归并排序优于快速排序的一个优势。


代码示例:从零实现完整归并排序

现在,我们将前面的函数组合起来,形成一个完整的可运行程序:

def merge_sort(arr):
    # 递归终止条件:数组长度 <= 1 时直接返回
    if len(arr) <= 1:
        return arr

    # 分割数组
    mid = len(arr) // 2
    left_half = arr[:mid]
    right_half = arr[mid:]

    # 递归排序左右两部分
    left_sorted = merge_sort(left_half)
    right_sorted = merge_sort(right_half)

    # 合并两个有序数组
    return merge(left_sorted, right_sorted)

def merge(left, right):
    result = []
    i = 0
    j = 0

    # 比较并合并两个有序数组
    while i < len(left) and j < len(right):
        if left[i] <= right[j]:
            result.append(left[i])
            i += 1
        else:
            result.append(right[j])
            j += 1

    # 添加剩余元素
    while i < len(left):
        result.append(left[i])
        i += 1

    while j < len(right):
        result.append(right[j])
        j += 1

    return result

if __name__ == "__main__":
    test_array = [64, 34, 25, 12, 22, 11, 90]
    print("原始数组:", test_array)
    sorted_array = merge_sort(test_array)
    print("归并排序结果:", sorted_array)

运行结果:

原始数组: [64, 34, 25, 12, 22, 11, 90]
归并排序结果: [11, 12, 22, 25, 34, 64, 90]

这个例子清晰地展示了归并排序的完整流程。从一个无序数组出发,经过层层分解与合并,最终得到有序结果。


时间与空间复杂度分析:为什么它高效?

理解一个算法的性能,离不开对复杂度的分析。我们来深入看看 Python 归并排序的时间与空间复杂度。

指标 分析
时间复杂度 始终为 O(n log n),无论最好、最坏还是平均情况
空间复杂度 O(n),需要额外的数组来存储合并过程中的临时数据

为什么是 O(n log n)?

  • 分解过程:每次将数组一分为二,总共需要 log n 层;
  • 每层需要遍历 n 个元素进行合并,所以每层是 O(n);
  • 总体复杂度为 O(n × log n)。

相比之下,冒泡排序是 O(n²),当数据量大时,归并排序的效率优势非常明显。

需要注意的是,归并排序不是原地排序(in-place),它需要额外的内存空间,这在内存受限的场景下可能是个缺点。但在大多数现代系统中,这种空间开销是可以接受的。


实际应用场景与优化建议

Python 归并排序虽然不是 Python 内置 sorted()list.sort() 的底层实现(它们使用 Timsort,一种混合排序算法),但它在以下场景中依然非常实用:

  • 对稳定性有要求的排序任务(如按年龄排序时,相同年龄的人按名字顺序保持不变);
  • 大数据量下的外部排序(数据无法全部加载到内存时,可分块归并);
  • 教学演示,帮助初学者理解分治思想。

✅ 优化建议:

  • 若数据量较小(如小于 50),可考虑使用插入排序替代递归基,提升性能;
  • 可以使用迭代方式实现归并排序,避免递归带来的函数调用开销;
  • 在合并阶段,可以预先分配结果数组,减少动态扩容。

总结:掌握归并排序,迈向算法进阶之路

Python 归并排序不仅仅是一个排序算法,更是一种思维方式的体现。它教会我们如何将一个复杂问题,拆解为更小的子问题,再通过组合子问题的解来解决原问题。

通过本文的讲解与代码实践,你应该已经掌握了:

  • 归并排序的基本原理与递归实现;
  • 如何正确编写 merge 函数完成合并操作;
  • 算法的时间与空间复杂度分析;
  • 实际应用中的注意事项。

当你在面试中被问到“请实现归并排序”时,不再只是背诵代码,而是能清晰地解释其背后的逻辑与优势。

算法的世界没有捷径,但每一步扎实的练习,都会让你离“写出优雅代码”的目标更近一步。希望这篇关于 Python 归并排序的深入解析,能成为你算法进阶路上的一块垫脚石。