java中有序数组合并是归并排序的“治”阶段,通过双指针线性扫描合并两个相邻有序子段,时间复杂度o(m+n),常用o(m+n)空间(临时数组),原地合并需严格条件;支持扩展至大规模外部排序。

在 Java 中,利用数组实现有序数组合并(即归并排序中的“归并段”操作),是大规模外部排序或分块排序的基础。核心在于:两个已升序排列的数组段,通过双指针线性扫描合并为一个有序数组,时间复杂度 O(m + n),空间复杂度可优化至 O(1)(原地合并需额外条件)或常用 O(m + n)(更清晰安全)。
理解归并段的基本逻辑
归并段不是对整个数组排序,而是将两个相邻的、各自有序的子数组(例如 arr[low..mid] 和 arr[mid+1..high])合并成一个有序段。这是归并排序的“治(Combine)”阶段,也是多路归并、外部排序中反复调用的关键操作。
- 不需要全局有序,只要求输入的两段内部有序
- 合并过程不依赖比较器以外的额外结构,适合内存受限场景
- 稳定(相等元素相对位置不变),适合对稳定性有要求的业务排序
标准双指针合并(推荐初学/通用场景)
使用额外临时数组暂存结果,避免覆盖原始数据,逻辑清晰、不易出错:
Java Maven 项目审查工作流的通用输入处理。当 Java Maven 项目以 ZIP压缩包或 GitLab 仓库 URL 形式提供且需要...时使用。
<font color="#888">// 合并 arr[low..mid] 和 arr[mid+1..high] 到临时数组,再拷回</font>
public static void merge(int[] arr, int low, int mid, int high) {
int[] temp = new int[high - low + 1];
int i = low, j = mid + 1, k = 0;
<pre class="brush:java;toolbar:false;">// 双指针遍历,取较小者填入 temp
while (i <p>}</p>
- 适用于任意长度、任意起始位置的有序子段合并
- 支持
int、long等基本类型,或泛型封装(配合Comparable) - 若用于大规模排序(如 10GB 文件分块排序后归并),此逻辑可迁移到磁盘缓冲区或流式读写
原地合并(仅限特定条件,慎用)
当后段空间足够容纳两段全部元素时(如 arr[0..m-1] 和 arr[m..m+n-1] 有序,且数组总长 ≥ m+n),可用三指针从尾部向前归并,实现 O(1) 额外空间:
<font color="#888">// 假设 arr[0..m-1] 和 arr[m..m+n-1] 有序,合并到 arr[0..m+n-1]</font>
public static void mergeInPlace(int[] arr, int m, int n) {
int i = m - 1, j = n - 1, k = m + n - 1;
while (i >= 0 && j >= 0) {
if (arr[i] > arr[m + j]) {
arr[k--] = arr[i--];
} else {
arr[k--] = arr[m + j--];
}
}
while (i >= 0) arr[k--] = arr[i--];
while (j >= 0) arr[k--] = arr[m + j--];
}
- 前提严格:后段必须预留足够空位,且索引需仔细偏移(如第二段实际起始为
m) - 实践中易引发越界或逻辑错误,不建议用于通用排序框架
- 更适合嵌入式或极端内存受限的归并场景(如内核模块)
扩展:支持大规模排序的实用技巧
面对 GB 级数据,纯内存归并不现实。可在数组思想上延伸为“归并段管理”:
-
分块排序 + 文件归并:先将大文件切分为内存可容纳的块(如 64MB),每块加载进数组 →
Arrays.sort()→ 写出为sorted_001.tmp等;再用PriorityQueue<bufferedreader></bufferedreader>维护多个文件的当前最小行,模拟多路归并 -
内存映射加速 I/O:对临时排序文件使用
FileChannel.map()映射为IntBuffer,减少拷贝,提升吞吐 -
自定义比较与对象归并:将数组替换为
Object[]或泛型方法,配合Comparator,支持复杂对象(如日志记录按时间戳归并)
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










