将两个有序的数组归并成一个更大的有序数组,这个操作叫归并。根据这个操作于是就有了归并排序,归并排序有两种实现方式。
自顶向下的归并排序
自顶向下的归并采用了分治(divide-and-conquer)的思想(将问题分(divide)成一些小的问题然后递归求解,然后治(conquer)阶段将分的阶段得到的各答案拼接在一起,即分而治之)。
代码实现
public class Merge {
private static Integer[] aux;
/**
* 初始化辅助数组和相应的上下限数值
*
* @param a
* @return
*/
public static Integer[] sort(Integer[] a) {
aux = new Integer[a.length]; // 初始化辅助数组
sort(a, 0, a.length - 1);
return a;
}
/**
* 辅助函数:用于交换元素
*
* @param valueA
* @param valueB
* @return
*/
public static Boolean less(Integer valueA, Integer valueB) {
return valueA < valueB;
}
/**
* 辅助函数:用于递归排序
*
* @param a
* @param lo
* @param hi
*/
private static void sort(Integer[] a, int lo, int hi) {
if (hi <= lo) return; // 极端情况 hi == lo,此时只有一个元素不需要再递归排序。
int mid = lo + (hi - lo) / 2;
sort(a, lo, mid);
sort(a, mid + 1, hi);
merge(a, lo, mid, hi); // 合并代码
}
/**
* 将数组中左右已排好序的两边重新合并到一起。
*
* @param a
* @param lo
* @param mid
* @param hi
*/
public static void merge(Integer[] a, int lo, int mid, int hi) {
int i = lo, j = mid + 1;
for (int k = lo; k <= hi; k++)
aux[k] = a[k];
for (int k = lo; k <= hi; k++)
if (i > mid) a[k] = aux[j++];
else if (j > hi) a[k] = aux[i++];
// aux[j] 和 aux[i] 循环两两进行比较,看谁先到达终点,然后执行上面两个分支。
else if (less(aux[j], aux[i])) a[k] = aux[j++];
else a[k] = aux[i++];
}
public static void main(String[] args) {
Integer[] array1 = new Integer[10];
for(int i = 0; i < array1.length; i++) {
array1[i] = (int)Math.floor((Math.random() * 10) + 1);
}
System.out.println(Arrays.toString(array1)); // [7, 5, 3, 8, 6, 3, 1, 10, 6, 9]
System.out.println(Arrays.toString(sort(array1))); // [1, 3, 3, 5, 6, 6, 7, 8, 9, 10]
}
}
自底向上归并排序
实现归并排序的另一种方法是先归并那些微型的数组,然后再成对归并得到子数组,如此这般,直到将整个数组归并在一起。
实现代码
/**
* 自低向上归并排序
*
* @param a
*/
public static Integer[] bUSort(Integer[] a) {
int N = a.length;
aux = new Integer[N];
for (int sz = 1; sz < N; sz = sz + sz) {
System.out.println("sz:" + sz);
// lo += sz + sz 因为是两两合并所以 sz + sz
// 数组长度 N 可能是奇数或者偶数
for (int lo = 0; lo < N - sz; lo += sz + sz) {
System.out.println(" lo: " + lo);
System.out.println(" lo+sz-1: " + (lo + sz - 1));
// Math.mi(lo+sz+sz-1, N-1) 兼容数组尾部的两两合并
merge(a, lo, lo + sz - 1, Math.min(lo + sz + sz - 1, N - 1));
}
}
return a;
}
参考: