归并排序:一种简单的利用递归排序的算法,将一个数组先(递归地)将它分成两半分别排序,然后将两半分别排序,然后将结果归并起来。
原地归并的抽象方法:
public static void merge(int[] a, int lo, int mid, int hi){
//将a[lo..mid] 和 a[mid+1..hi] 归并
int N = a.length;
int[] b = new int[N];
int i = lo;
int j = mid + 1;
for (int k = lo; k <= hi; k++) {
b[k] = a[k];
}
//归并回a[lo..hi]
for (int k = lo; k <= hi; k++) {
if(i > mid){
a[k] = b[j++];
}else if(j > hi){
a[k] = b[i++];
} else if(b[i] < b[j]){
a[k] = b[i++];
}else{
a[k] = b[j++];
}
}
}
先将所有元素复制到吧b[]中,然后再归并回a[]中。
归并轨迹如下图:
自顶向下的归并排序:
这是基于原地归并的抽象实现的递归归并,应用了高效算法设计中的分治思想的典型的例子。
public static void sort(int[] a, int lo, int hi){ // lo,hi 数组下标
if(hi <= lo){
return;
}
int mid = lo + (hi-lo)/2;
sort(a, lo, mid); //将左半边排序
sort(a, mid+1, hi); //将右半边排序
Merge.merge(a, lo, mid, hi); //原地归并的抽象方法
}
要对数组 a[lo..hi] 进行排序,先将它分为a[lo..mid] 和 a[mid+1..hi] 两部分,分别通过递归调用将它们单独排序,最后将有序的子数组归并为最终的排序结果。
归并结果轨迹:
自底向上的归并排序:
这种排序的思想是:先归并那些微型数组,然后再成对归并得到的子数组。
先进行两两归并,然后再四四归并,再八八归并。。。
public static void sort(int[] a){
int N = a.length;
for (int sz = 1; sz < N; sz = 2*sz) { //子数组的大小
for (int lo = 0; lo < N - sz; lo += 2*sz) { // lo为子数组的索引
Merge.merge(a, lo, lo + sz - 1, Math.min(N - 1, lo + 2*sz -1));
}
}
}
归并轨迹图:
特点:
对于长度为N的任意数组,归并排序需要(1/2)N/lgN至NlgN次比较,最多需要访问数组6NlgN次。
可以用归并排序处理数百万甚至更大规模的数组。