如果你和我一样,曾经为了搞懂排序算法,在纸上把快排、归并、堆排的递归树画过无数遍,八成会对一件事产生执念:分治排序,好像只有二分这一条路。二分确实漂亮,log₂ n 的递归深度,每一层合并代价 O(n),整个算法 O(n log n)。但我一直有个疑问:分治的关键,真的在于“切成两半”吗?如果我把问题切成三段、四段,甚至完全按照值域去切,而不是机械地按数组位置切,合并的时候是不是能省掉很多逐项比较?
带着这个疑问,我折腾了一个原创排序算法,取名叫 SplitMergeSort。它不是传统意义上的二分分治,也不是简单地把归并排序改成三分归并。核心思路是:把待排序数组按两个分界值切成三块,让三块的值域天然有序,递归排序后直接拼接,从而把传统归并排序中最耗时的“逐项比较合并”压缩成近乎零成本的操作。这篇文章我会讲清楚它的设计动机、完整原理、工程实现、复杂度分析,以及我实测和踩坑的过程。适合对排序算法原理感兴趣、想跳出教科书二分框架的读者,也适合当成一个“分治排序还能怎么玩”的思路参考。
1. 从二分的惯性里跳出来:SplitMergeSort 的设计动机
1.1 传统分治排序的二分惯性
我们在教科书里学的分治排序,几乎全在二分框架里打转。
归并排序把数组从中间劈开,递归处理左右半边,最后两路归并。快速排序也是选一个枢轴,把数组分成小于枢轴和大于枢轴两部分,再分别递归。主定理告诉我们,T(n) = 2T(n/2) + O(n) 的解是 O(n log n),而且是渐进意义下的最优。所以大家都默认,二分是最合理、最漂亮的分治方式。
但这个“最优”是有前提的:它假设每个子问题的规模差不多,且合并或分区代价是线性的。它没有考虑数据的实际分布。换句话说,二分只是把“位置”切开了,对“值”本身视而不见。
我并不是说二分不对。我只是好奇,当数据本身具有明显分布特征时,能不能通过改变分治的切法,让某些步骤的成本直接降为零。
1.2 二分不一定是最优的分治策略
二分在逻辑上简洁,但在某些场景下很浪费。
先看归并排序。即使左半段的最大值已经小于右半段的最小值——也就是说两个子段整体上已经有序了——标准两路归并依然要逐项比较左右两边的元素,把每个元素都搬到辅助数组里。这种“明知有序还要比较一遍”的浪费,普遍存在。
再看快速排序。如果数组里存在
