归并:将俩个有序的数组归并成一个更大的有序数组。很快人们就根据归并的这个操作发明了一种简单的递归排序算法:归并排序。 要将一个数组排序,可以先(递归地)将它分成俩半分别排序,然后将结果归并起来。
优点:将任意长度为 N 的数组排序所需时间和 NlogN 成正比;
缺点:所需的额外空间和 N 成正比。
原地归并抽象方法:
public static void merge(Comparable[] a, int lo, int mid, int hi){
//将a[lo..mid] 和 a[mid+1..hi] 归并,俩个有序数组
int i = lo, j = mid + 1;
//辅助数组
for(int k = lo; k <= hi; k++) //将a[lo..hi] 复制到aux[lo..hi]
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++]; //右半边取尽(取左半边元素)
else if( less(a[i], a[j]) ) a[k] = aux[i++]; //左半边的当前元素小于右半边的当前元素(取左半边元素)
else a[k] = aux[j++]; //左半边的当前元素大于等于右半边的当前元素(取右半边元素)
}
}
自顶向下的归并排序
基于原地归并的抽象实现了一种递归归并,这也是应用高效算法设计中分治思想的最典型的例子。
public class Merge{
private static Comparable[] aux; //归并所需的辅助数组
public static void sort(Comparable[] a){
aux = new Comparable[a.length]; //一次性分配空间
sort(a, 0, a.length - 1);
}
public static void sort(Comparable[] a, int lo, int hi){
//将a[lo..hi] 排序
if(lo >= hi) return; //结束递归
int mid = (lo + hi)/2;
sort(a, 0, mid); //将左半边排序
sort(a, mid + 1, hi); //将右半边排序
merge(a, lo, mid, hi); //归并结果(代码见 “原地归并抽象方法”)
}
}
自底向上的归并排序
public class MergeBU{
private static Comparable[] aux;
public static void sort(Comparable[] a){
//进行lgN 次俩俩归并
int N = a.length;
aux = new Comparable[N];
for(int sz = 1; sz < N; sz = sz*2) //sz 子数组的大小,2的幂次方
for(int lo = 0; lo < N-sz; lo += sz*2) //lo:子数组索引
merge(a, lo, lo+sz-1, Math.min(lo+sz+sz-1, N-1);
}
}
归并排序是一种渐进最优的基于比较排序的算法。