题目描述
思路解析动画文字版
对半切谁都会,取中点就行。归并真正的动作在“合”:两段已经各自有序,要并成一个有序段。因为两段已有序,用双指针扫一遍就能并完,不用重新排。
左段指针、右段指针各指向自己当前最小的没取的数。比两个头,小的先放进结果、对应指针前进;一段取完,另一段剩下的直接接上。和合并两条有序链表一模一样。
第 1 趟 · 宽度 1→2:第 1 趟开始 · 先把相邻单个两两合并。聚焦下标 0-1 的 [5,2],单个元素天然有序,现在合它俩。
第 1 趟 · 合 [5][2]:比 5 和 2 · 取右头 2
第 1 趟 · 合 [5][2]:取 2 放入位置 0 · 本段结果 [2]
第 1 趟 · 合 [5][2]:本段合并完成 · [2,5] 有序
第 1 趟 · 合 [8][1]:比 8 和 1 · 取右头 1
第 1 趟 · 合 [8][1]:取 1 放入位置 2 · 本段结果 [1]
第 1 趟 · 合 [8][1]:本段合并完成 · [1,8] 有序
第 1 趟 · 合 [9][3]:比 9 和 3 · 取右头 3
第 1 趟 · 合 [9][3]:取 3 放入位置 4 · 本段结果 [3]
第 1 趟 · 合 [9][3]:本段合并完成 · [3,9] 有序
第 1 趟 · 合 [7][4]:比 7 和 4 · 取右头 4
第 1 趟 · 合 [7][4]:取 4 放入位置 6 · 本段结果 [4]
第 1 趟 · 合 [7][4]:本段合并完成 · [4,7] 有序
第 1 趟完成:第 1 趟结束 · 得到 4 个二元有序段:[2,5] [1,8] [3,9] [4,7]。下一趟把相邻两个二元段合成四元段。
第 2 趟 · 合 [2,5]+[1,8]:比 2 和 1 · 取右头 1
第 2 趟 · 合 [2,5]+[1,8]:取 1 放入位置 0 · 本段结果 [1]
第 2 趟 · 合 [2,5]+[1,8]:比 2 和 8 · 取左头 2
第 2 趟 · 合 [2,5]+[1,8]:取 2 放入位置 1 · 本段结果 [1,2]
第 2 趟 · 合 [2,5]+[1,8]:比 5 和 8 · 取左头 5
第 2 趟 · 合 [2,5]+[1,8]:取 5 放入位置 2 · 本段结果 [1,2,5]
第 2 趟 · 合 [2,5]+[1,8]:本段合并完成 · [1,2,5,8] 有序
第 2 趟 · 合 [3,9]+[4,7]:比 3 和 4 · 取左头 3
第 2 趟 · 合 [3,9]+[4,7]:取 3 放入位置 4 · 本段结果 [3]
第 2 趟 · 合 [3,9]+[4,7]:比 9 和 4 · 取右头 4
第 2 趟 · 合 [3,9]+[4,7]:取 4 放入位置 5 · 本段结果 [3,4]
第 2 趟 · 合 [3,9]+[4,7]:比 9 和 7 · 取右头 7
第 2 趟 · 合 [3,9]+[4,7]:取 7 放入位置 6 · 本段结果 [3,4,7]
第 2 趟 · 合 [3,9]+[4,7]:本段合并完成 · [3,4,7,9] 有序
第 2 趟完成:第 2 趟结束 · 得到 2 个四元有序段:[1,2,5,8] 和 [3,4,7,9]。最后一趟把它俩合成全有序。
第 3 趟 · 合两个四元段:比 1 和 3 · 取左头 1
第 3 趟 · 合两个四元段:取 1 放入位置 0 · 本段结果 [1]
第 3 趟 · 合两个四元段:比 2 和 3 · 取左头 2
第 3 趟 · 合两个四元段:取 2 放入位置 1 · 本段结果 [1,2]
第 3 趟 · 合两个四元段:比 5 和 3 · 取右头 3
第 3 趟 · 合两个四元段:取 3 放入位置 2 · 本段结果 [1,2,3]
第 3 趟 · 合两个四元段:比 5 和 4 · 取右头 4
第 3 趟 · 合两个四元段:取 4 放入位置 3 · 本段结果 [1,2,3,4]
第 3 趟 · 合两个四元段:比 5 和 7 · 取左头 5
第 3 趟 · 合两个四元段:取 5 放入位置 4 · 本段结果 [1,2,3,4,5]
第 3 趟 · 合两个四元段:比 8 和 7 · 取右头 7
第 3 趟 · 合两个四元段:取 7 放入位置 5 · 本段结果 [1,2,3,4,5,7]
第 3 趟 · 合两个四元段:比 8 和 9 · 取左头 8
第 3 趟 · 合两个四元段:取 8 放入位置 6 · 本段结果 [1,2,3,4,5,7,8]
第 3 趟 · 合两个四元段:本段合并完成 · [1,2,3,4,5,7,8,9] 有序
排序完成:全部合并完成 · 整个数组 [1,2,3,4,5,7,8,9] 从小到大有序。这就是归并排序:分到底,再一层层合上来。
三个高频追问:稳定性来源、和快排的取舍、合并有序这个内核的复用场景。
参考代码
def merge_sort(a): if len(a) <= 1: return a # 单个天然有序 mid = len(a) // 2 L = merge_sort(a[:mid]) # 分 + 递归排左 R = merge_sort(a[mid:]) # 分 + 递归排右 res, i, j = [], 0, 0 while i < len(L) and j < len(R): # 合并:谁小取谁 if L[i] <= R[j]: res.append(L[i]); i += 1 else: res.append(R[j]); j += 1 return res + L[i:] + R[j:] # 接上剩余残段复杂度
- 时间复杂度:O(n log n),log n 趟合并,每趟把 n 个元素各扫一次,与输入顺序无关
- 空间复杂度:O(n),合并需要一个等长的临时数组装结果
易错点
面试追问把动画讲成自己的话
追问归并排序为什么是稳定的?
追问它和快速排序怎么选?
追问“合并两个有序”这个子过程还能用在哪?
这道题到这就讲完了。动画和文字是同一套思路——别光看,关掉页面自己默写一遍。然后顺着主线继续:
堆排序
困难 · 沿着 排序套路 继续往下推进
把这道题真正学会,再走
图解算法年卡 ¥99 /年
- ✓本题每步动画的吴师兄语音讲解(全站陆续覆盖)
- ✓小欧带 8 步通关训练——追问到不看答案也能写对、能 30 秒讲给面试官听
- ✓学习报告,记录每道题的掌握程度
76k+ GitHub Star · 吴师兄开源图解算法,几十万开发者在看的算法讲解
想成体系刷透这类套路?去图解算法专题