图解题库 / 排序 · 归并

归并排序

中等 含交互动画

学完这道你应该能: 说清它为什么用「归并」,而不是死记步骤; 合上页面,自己默写出核心代码; 用 30 秒把思路讲清楚。

题目描述

:对半切到只剩单个元素(天然有序)。:把相邻的两个有序段合并成更大的有序段,直到合成整个数组。

输入 = [5, 2, 8, 1, 9, 3]
输出 = [1, 2, 3, 5, 8, 9]

思路解析

两个有序段各放一个指针,比较两个指针处的值,小的放进结果、该指针前进,直到一段取完,另一段剩余直接接上——和合并两个有序链表一模一样。

对半切分:先把数组切成左半 [5,2,8] 和右半 [1,9,3],各自再递归切分、排序。

两半各自排好:递归到底再合并,左半排成 [2,5,8]、右半排成 [1,3,9]。现在合并这两个有序段。

合并 · 比 2 和 1 → 取 1:左指针指 2、右指针指 1:1 更小,先取 1。结果:[1]。右指针前进到 3。

比 2 和 3 → 取 2,再取 3:比 2 和 3:取 2;再比 5 和 3:取 3。结果累积:[1,2,3]。谁小取谁,指针各自前进。

合并完成:继续比 5、8 和 9,依次取出,合并成 [1,2,3,5,8,9]。整个数组有序。

归并是“分治”的标准范本:把大问题对半拆、解决子问题、再合并。“合并两个有序”这个子过程(21 题)是它的核心积木,也用于求逆序对。

参考代码(Python)

Python
1def merge_sort(a):
2    if len(a) <= 1: return a            # 单个天然有序
3    mid = len(a) // 2
4    L = merge_sort(a[:mid])            # 分 + 递归排左
5    R = merge_sort(a[mid:])           # 分 + 递归排右
6    res, i, j = [], 0, 0
7    while i < len(L) and j < len(R):   # 合并:谁小取谁
8        if L[i] <= R[j]: res.append(L[i]); i += 1
9        else: res.append(R[j]); j += 1
10    return res + L[i:] + R[j:]         # 接上剩余

复杂度分析

  • 时间复杂度:O(n log n) —— 稳定保证,与输入无关
  • 空间复杂度:O(n) —— 合并需要额外数组

套路模板

记住骨架:双指针谁小取谁、相等取左保稳定、最后接残段。这个“合并有序”是归并排序、求逆序对、合并K个有序的共同内核。

Python
1i = j = 0; res = []
2while i < len(L) and j < len(R):
3    if L[i] <= R[j]: res.append(L[i]); i += 1   # 等时取左=稳定
4    else: res.append(R[j]); j += 1
5res += L[i:] + R[j:]                  # 接残段

易错点

  • 相等时取右 → ✅ 相等取左 L[i] <= R[j](取左才稳定(相等元素相对顺序不变))
  • 忘了接剩余段 → ✅ res + L[i:] + R[j:](一段取完后另一段剩余要补上)

以上文字与上方动画完全同一思路:先看动画建立直觉 → 读文字巩固 → 关掉页面自己默写一遍代码,能写出来才算真的掌握

下一题 →堆排序 ← 返回题库