归并:即将两个有序的数组归并成一个更大的有序数组。
原地归并的抽象方法
该方法用两个指针分别比较数组左右两边的元素大小。该方法创建了一个新的数组用来储存原来数组的信息。
public static void merge(Comparable[] a, int lo, int mid, int hi){
int[] aux = new int[a.length];
int i = lo, j = mid + 1;
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[j],a[i])){
a[k] = aux[j++];
}else{
a[k] = aux[i++];
}
}
}
自顶向下的归并排序
package sec2;
public class Merge {
private static Comparable[] aux; //归并所需的辅助数组
public void sort(Comparable[] a){
aux = new Comparable[a.length];
sort(a, 0, a.length - 1);
}
private static void sort(Comparable[] a, int lo, int hi){
if(hi <= lo){
return;
}
int mid = lo + (hi - lo) / 2;
sort(a, lo, mid);
sort(a, mid + 1, hi);
merge(a, lo, mid, hi);
}
private static boolean less(Comparable v,Comparable w){
return v.compareTo(w) < 0;
}
public static void merge(Comparable[] a, int lo, int mid, int hi){
int[] aux = new int[a.length];
int i = lo, j = mid + 1;
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[j],a[i])){
a[k] = aux[j++];
}else{
a[k] = aux[i++];
}
}
}
}
对于长度为N 的任意数组,自顶向下的归并排序需要1/2NlgN至NlgN次比较。
C(N)表示将一根长度为N的数组排序时所需要的比较次数。且C(0) = C(1) = 0
C(N) <= C(N / 2) + C(N / 2) + N(左边排序的比较次数+右边排序的比较次数+归并要比较的最大次数)
C(N) >= C(N / 2) + C(N / 2) + (N / 2)(左边排序的比较次数+右边排序的比较次数+归并要比较的最小次数)
假设N = 2^n,当C(N)取最大值的时候
C(2^n) = 2C(2^(n - 1)) + 2^n
C(2^n) / 2^n = C(2^(n - 1)) / 2^(n - 1) + 1
将这个式子带入右边得
C(2^n) / 2^n = C(2^(n - 2)) / 2^(n - 2) + 2
......
C(2^n) / 2^n = C(2^0) / 2^0 + n
C(N) = NlgN
对于长度为N的任意数组,自顶向下的归并排序最多需要访问数组6NlgN次。
每次归并最多需要访问数组6N次(2N次用来复制,2N次用来将排序好的元素移动回去,另外最多比较2N次)