V

算法可视化

ZHEN
分享到微博分享到 XGitHub 仓库个人主页
学习工具算法复杂度速查算法学习路径
数据结构数组链表栈队列树堆哈希表图字典树并查集LRU 缓存跳表线段树B+ 树布隆过滤器树状数组
经典排序算法冒泡排序鸡尾酒排序双调排序选择排序插入排序二分插入排序希尔排序归并排序自顶向下归并快速排序三路快排双轴快排堆排序计数排序基数排序桶排序
图算法Dijkstra 最短路Kruskal 最小生成树Prim 最小生成树Bellman-Ford 最短路拓扑排序Floyd 多源最短路强连通分量2-SAT最大流二分图匹配LCA 倍增欧拉路径
动态规划编辑距离0-1 背包完全背包最长公共子序列最长递增子序列硬币找零方案数石子合并旅行商 TSP树形 DP数位 DP换根 DP
回溯与搜索N 皇后子集生成全排列组合总和迷宫寻路岛屿数量单词搜索数独A* 寻路
字符串KMP 字符串匹配Rabin-KarpBoyer-MooreManacher后缀数组LCP / height 数组AC 自动机Z 函数
数学与数论埃氏筛线性筛欧几里得算法快速幂扩展欧几里得中国剩余定理欧拉函数米勒-拉宾FFTPollard's Rho
计算几何凸包旋转卡壳最近点对线段相交扫描线求交
查找二分查找二分边界旋转数组搜索二分答案三分查找

自顶向下归并 Top-Down Merge Sort

归并排序 · 递归分治 · 与迭代版对照

归并排序的另一种写法

站内的归并排序页走的是自底向上路线:不递归,段宽从 1 开始一轮轮翻倍合并。本页是同一算法的另一种经典写法——自顶向下(递归分治):sort(lo, hi) 把区间对半劈开,先递归排好左半、再递归排好右半,回程时把两个有序半段合并成一段。递归到单个元素天然有序,就是分治的「底」。

递归怎么走:看调用栈

递归的执行轨迹就是一棵分治树,而机器执行它靠的是调用栈。下面播放器里右侧的栈轨显示的正是当前的递归调用链:压栈 = 下钻(区间对半、进入子问题),栈顶 = 正在处理的区间,合并完成后弹栈 = 返回上一层。温度计式的 temp 轨(同归并排序页)则展示每次合并:两段比较取小、依次写入 temp、整段拷回。

下面固定 8 个数 [6, 3, 8, 1, 9, 2, 7, 4]。点「下一步」:看栈先压到 [0,7] → [0,3] → [0,1] 三层深,才做第一次合并 [6],[3] → [3,6];随后逐层回程合并、栈逐层收缩,最后合并 [1,3,6,8] 与 [2,4,7,9] 得到全序。

6
3
8
1
9
2
7
4
区间栈 · 每格 = 一段待排序子数组 a[lo..hi](栈顶先弹出分区)
a[0..7]

下钻 [0,7]:对半为 [0,3] 与 [4,7]

1function mergeSort(a: number[], lo = 0, hi = a.length - 1): number[] {
2 if (lo >= hi) return a;
3 const mid = (lo + hi) >> 1;
4 mergeSort(a, lo, mid);
5 mergeSort(a, mid + 1, hi);
6 const temp: number[] = [];
7 let i = lo, j = mid + 1;
8 while (i <= mid && j <= hi) {
9 if (a[i] <= a[j]) {
10 temp.push(a[i++]);
11 } else {
12 temp.push(a[j++]);
13 }
14 }
15 while (i <= mid) temp.push(a[i++]);
16 while (j <= hi) temp.push(a[j++]);
17 for (let k = 0; k < temp.length; k++) a[lo + k] = temp[k];
18 return a;
19}
n8
深度1
lo0
mid3
hi7
i-
j-
k-
a[i]-
a[j]-
writeCount0
1 / 63

复杂度与两种写法的取舍

与自底向上完全一致:时间 O(n log n)(每层合并共 O(n)、层数 log n)、额外空间 O(n)(temp)、稳定。差别在驱动方式:递归版代码更贴近「分治」的思维模型,是教科书与面试的标准版本;迭代版省去递归调用开销、也不吃栈深,工程里两者都常见。

自顶向下(本页):递归下钻对半分,回程逐层合并——分治思想的标准载体,配调用栈看最清楚。
自底向上(归并排序页):段宽 1→2→4→8 迭代翻倍——同一个 merge,不递归、免栈深。
两页的合并过程一模一样,对照着单步走一遍,「归并」就吃透了。

递归 + 合并的组合还会在 TimSort(Python/Java 实际使用的排序)里再升级——识别天然有序段再归并。那是排序线后面的进阶篇。