归并排序体现了 “分而治之” 的算法思想,具体为:
如下图所示,为数组
[7,3,2,6,0,1,5,4]的归并排序过程。

递归划分:
计算数组中点 ,递归划分左子数组 merge_sort(l, m) 和右子数组 merge_sort(m + 1, r) ;
当 时,代表子数组长度为 1 或 0 ,此时 终止划分 ,开始合并;
合并子数组:
暂存数组 闭区间 内的元素至辅助数组 ;
循环合并: 设置双指针 , 分别指向 的左 / 右子数组的首元素;
注意: 子数组的左边界、中点、右边界分别为 , , ,而辅助数组 中的对应索引为 , , ;
如下动图所示,为数组
[7,3,2,6]的归并排序过程。
为简化代码,「当 时」 与 「当 时」 两判断项可合并。
def merge_sort(nums, l, r):
# 终止条件
if l >= r: return
# 递归划分数组
m = (l + r) // 2
merge_sort(nums, l, m)
merge_sort(nums, m + 1, r)
# 合并子数组
tmp = nums[l:r + 1] # 暂存需合并区间元素
i, j = 0, m - l + 1 # 两指针分别指向左/右子数组的首个元素
for k in range(l, r + 1): # 遍历合并左/右子数组
if i == m - l + 1:
nums[k] = tmp[j]
j += 1
elif j == r - l + 1 or tmp[i] <= tmp[j]:
nums[k] = tmp[i]
i += 1
else:
nums[k] = tmp[j]
j += 1
# 调用
nums = [3, 4, 1, 5, 2, 1]
merge_sort(0, len(nums) - 1)









时间复杂度: 最佳 ,平均 ,最差 。
空间复杂度 : 合并过程中需要借助辅助数组 ,使用 大小的额外空间;划分的递归深度为 ,使用 大小的栈帧空间。
若输入数据是 链表 ,则归并排序的空间复杂度可被优化至 ,这是因为:
通过应用「双指针法」,可在 空间下完成两个排序链表的合并,省去辅助数组 使用的额外空间;
通过使用「迭代」代替「递归划分」,可省去递归使用的栈帧空间;
详情请参考:148. 排序链表
非原地: 辅助数组 需要使用额外空间。
稳定: 归并排序不改变相等元素的相对顺序。
非自适应: 对于任意输入数据,归并排序的时间复杂度皆相同。