概述
非递归实现归并排序,通常被称为'自底向上'(Bottom-Up)的归并排序。与递归版本不同,非递归版本不依赖系统栈回溯,而是直接从最小的子数组(长度为 1)开始,两两合并,然后步长翻倍(2, 4, 8...),直到整个数组有序。
核心思路
将递归逻辑改写为循环结构主要有两种常见方法:使用栈模拟或改为迭代。但在归并排序中,单纯用栈模拟往往只能处理拆分,难以在弹出时自然触发合并操作。因此,我们采用直接控制步长的循环方式。
核心原理如下:
- 步长(gap)设为 1:首先将数组看作由 N 个长度为 1 的有序子数组组成。
- 两两合并:相邻的两个长度为 1 的子数组合并成长度为 2 的有序子数组。
- 步长翻倍:将步长设为 2,相邻的两个长度为 2 的子数组合并成长度为 4 的有序子数组。
- 重复:步长继续翻倍(4, 8, 16...),直到步长超过数组长度。
简单理解,就像整理一副乱序的扑克牌:先两两配对排好,再把这两张一组的两组拼成四张一组,以此类推,直到整副牌有序。
实现流程
假设存在数组 [10, 6, 7, 1, 3, 9, 4, 2],我们通过 gap 的变化来演示过程。
第一轮:gap = 1
此时我们将相邻的单元素视为一组进行归并。
- 第一次合并:左
[10],右[6]。6 小于 10,交换位置。结果:[6, 10, 7, 1, 3, 9, 4, 2] - 第二次合并:左
[7],右[1]。1 小于 7,交换位置。结果:[6, 10, 1, 7, 3, 9, 4, 2] - 第三次合并:左
[3],右[9]。已有序,无需变动。 - 第四次合并:左
[4],右[2]。2 小于 4,交换位置。
第一轮结束,数组变为两两有序:[6, 10, 1, 7, 3, 9, 2, 4]。
第二轮:gap = 2
此时将相邻的长度为 2 的有序段合并。
- 第一次归并:左
[6, 10],右[1, 7]。比较后取 1, 6, 7, 10。 - 第二次归并:左
[3, 9],右[2, 4]。比较后取 2, 3, 4, 9。
第二轮结束,数组变为四四有序:[1, 6, 7, 10, 2, 3, 4, 9]。
第三轮:gap = 4
最后将两个长度为 4 的有序段合并。
- 第一次归并:左
[1, 6, 7, 10],右[2, 3, 4, 9]。依次比较取出最小值。
第三轮结束,数组完全有序:[1, 2, 3, 4, 6, 7, 9, 10]。
边界处理
归并排序的逻辑基于'完全二叉树',当数组长度是 2 的幂次时,每一步都能完美均分。但现实中的数组长度往往不是 2 的整数次幂,这会导致边界问题。
不均分引起的问题
当步长倍增时,可能会出现以下情况:
- 数组落单:当
begin2 >= n时,说明右侧没有对应的子数组了,左侧剩下的元素已经是有序的,无需归并。


