归并排序的思想
将数组中的元素递归地拆分为左右两部分,在左右两部分都排好序后,再将两个子数组归并为一个有序的大数组。归并排序是分治法的典型应用。
以下是原地归并且自顶向下归并的例子,归并的时间复杂度是NlogN
.
public void sort(Comparable[] a) {
Comparable[] b = new Comparable[a.length];
sort(a, 0, a.length-1, b);
show(a);
}
void sort(Comparable[]a, int lo, int hi, Comparable[] b){
//递归拆分数组,直到只有两个元素
if(lo>=hi)
return;
int mid = lo + (hi-lo)/2;
//左边排序
sort(a, lo, mid, b);
//右边排序
sort(a,mid+1, hi, b);
//左右归并
merge(a, lo, mid, hi, b);
}
/**
* 归并
* @param a
* @param low
* @param mid 左边数组的右端下标
* @param hi
*/
void merge(Comparable[] a, int low,int mid,int hi, Comparable[] b) {
//将数组元素复制到一个新数组中
for(int i=low;i<=hi;i++){
b[i] = a[i];
}
int m=low;
int n=mid+1;
//归并回原数组,由于两边数组都是已排序的,所以只需取出两者头部最小的依次放入原数组
for(int i=low;i<=hi;i++) {
if (m > mid)
a[i] = b[n++];
else if(n > hi)
a[i] = b[m++];
else if(less(b[m],b[n]))
a[i] = b[m++];
else
a[i] = b[n++];
}
}
注意:以上归并首先是将原数组复制到数组b中,数组b写成层层传参,是为了复用,避免了每次归并都需要创建一个新数组。实际上也可以用其它方法保证复用。
还有另一种自底向上的归并,区别是先归并所有小数组。如先将所有2个元素的数组归并完成,再将所有相邻的数组归并为4元素,再全部归并为8元素。。。与上边始终是先解决完左边部分再从头解决右边部分有所区别
自顶向下:化整为零递归解决问题
自底向上:就是循序渐进的整体向前推进