前言
非递归实现的归并排序通常被称为'自底向上'(Bottom-Up)版本。与递归版本不同,它不依赖栈回溯拆分,而是直接从长度为 1 的子数组开始,两两合并,步长不断翻倍(1, 2, 4...),直到覆盖整个数组。
核心思路
将递归转换为非递归主要有两种路径:用栈模拟递归过程,或直接改写为循环结构。
为什么不用栈模拟?
单纯用栈模拟递归只能完成'拆分',无法自然完成'合并'。
假设我们要排序数组 [8, 4, 5, 7]。如果只用栈记录区间 [0, 3],弹出后拆分为 [0, 1] 和 [2, 3] 压入栈。当处理完子区间 [0, 0] 和 [1, 1] 时,栈里已经没有 [0, 1] 这个父节点了,程序不知道这两个单元素需要合并。之前的父任务被丢弃,导致合并指令丢失。
因此,非递归版本的核心在于循环结构。我们不再拆分,而是直接合并。从最小有序子序列(长度 1)开始,相邻两个合并成长度 2 的有序序列,再合并成长度 4,以此类推。
算法流程
- 初始化步长 (gap):设为 1,表示当前有序子序列的长度。
- 两两合并:遍历数组,将相邻的两个长度为
gap的子数组合并。 - 步长翻倍:
gap *= 2,重复上述步骤。 - 终止条件:当
gap >= n时,整个数组已有序。
想象面前有一排乱序的扑克牌:先两两整理成有序的 2 张一组,再把这 2 张一组看作整体,两两整理成 4 张一组,直到整副牌有序。
实现细节演示
以数组 [10, 6, 7, 1, 3, 9, 4, 2] 为例,逐步观察 gap 变化时的归并过程。
第一轮:gap = 1
此时将数组视为 8 个长度为 1 的有序子数组。
- 第 1 次合并:左
[10],右[6]。比较后交换,结果[6, 10]。 - 第 2 次合并:左
[7],右[1]。比较后交换,结果[1, 7]。 - 第 3 次合并:左
[3],右[9]。已有序,无需变动。 - 第 4 次合并:左
[4],右[2]。比较后交换,结果[2, 4]。
第一轮结束,数组变为两两有序:[6, 10, 1, 7, 3, 9, 2, 4]。
第二轮:gap = 2
此时将数组视为 4 个长度为 2 的有序子数组。
- 第 1 次归并:左
[6, 10],右[1, 7]。标准归并逻辑:取 1, 6, 7, 10。 - 第 2 次归并:左
[3, 9],右[2, 4]。标准归并逻辑:取 2, 3, 4, 9。
第二轮结束,数组变为四四有序:[1, 6, 7, 10, 2, 3, 4, 9]。
第三轮:gap = 4
此时将数组视为 2 个长度为 4 的有序子数组。
- :左 ,右 。依次比较取出,最终得到完整有序数组 。


