从 1 个元素开始归并,逐渐向上归并到全部元素。

链表排序,就可以使用这种「自底向上的归并排序」思想。但是又因为链表不能通过下标来划分子序列,所以操作上和数组还是有些区别的,但是思路是一样的。详见 LeetCode 148 号问题:排序链表

public class MergeSortBU {
// 和自顶向下的归并排序中的 merge 函数一样
private static void merge(Comparable[] arr, int l, int mid, int r) {
Comparable[] aux = Arrays.copyOfRange(arr, l, r + 1);
int i = l, j = mid + 1;

for (int k = l; k <= r; k++) {
if (i > mid) {
arr[k] = aux[j - l];
j++;
} else if (j > r) {
arr[k] = aux[i - l];
i++;
} else if (aux[i - l].compareTo(aux[j - l]) < 0) {
arr[k] = aux[i - l];
i++;
} else {
arr[k] = aux[j - l];
j++;
}
}
}

public static void sort(Comparable[] arr) {
int n = arr.length;

// 优化:对于小数组, 使用插入排序
for (int i = 0; i < n; i += 16)
InsertionSort.sort(arr, i, Math.min(i + 15, n - 1));

// sz:元素个数
for (int sz = 16; sz < n; sz <<= 1) {
// i:每轮操作的起点元素位置,每次归并 2*sz 个元素
for (int i = 0; i < n - sz; i += sz + sz) {
// 优化:对于 arr[mid] <= arr[mid+1] 的情况,不进行 merge
// 详见:https://xuguangwei.com/posts/3037455158/
if (arr[i + sz - 1].compareTo(arr[i + sz]) > 0) {
// merge(arr, left, mid, right)
// 防止剩下的右半部分不够 sz 个,所以用了 min 方法。
merge(arr, i, i + sz - 1, Math.min(i + sz + sz - 1, n - 1));
}
}
}
}
}