Ex2_2_11归并排序优化
优化一:加快小数组排序:
递归会使小规模问题中的方法调用过于频繁,可以规定,当lo和hi的差值小于某一阈值时就改用插入排序,只需要替换一句话
//if (hi <= lo) return;
if (hi - lo <= 15) {
Insertion(src, lo, hi);
return;
}
优化二:测试数组是否已经有序:
可以增加一个判断,如果a[mid]<=a[mid+1]则认为数组已经是有序的(以为此时左右两部分已经是排好序的),并跳过merge()方法,此时任意有序子数组算法运行时间就变成了线性的.
if (less(aux[mid], aux[mid + 1])) {
System.arraycopy(aux, lo, src, lo, hi + 1 - lo);//如果已经有序,只需复制过来就好
return;
}
merge(src, aux, lo, mid, hi);
优化三:不将元素复制到辅助数组:
原来merge()方法是将原数组src复制一份到aux后,再将aux的元素按顺序归并回src,但可以优化这一步,节省将数组元素复制到aux的时间(但空间不行),这需要在递归的每层交换输入数组和辅助数组的参数位置
private static void sort(Comparable[] src, Comparable[] aux, int lo, int hi) {
//if (hi <= lo) return;
if (hi - lo <= 15) {
Insertion(src, lo, hi);
return;
}
int mid = lo + (hi - lo) / 2;
sort(aux, src, lo, mid);//交换角色
sort(aux, src, mid + 1, hi);
if (less(aux[mid], aux[mid + 1])) {
System.arraycopy(aux, lo, src, lo, hi + 1 - lo);//如果已经有序,只需复制过来就好
return;
}
merge(src, aux, lo, mid, hi);//保持原顺序,确保最终是src被排好序
}
完整代码
import edu.princeton.cs.algs4.In;
import edu.princeton.cs.algs4.StdOut;
public class Ex2_2_11 {
private static Comparable[] aux;
private static boolean less(Comparable v, Comparable w){
return v.compareTo(w) < 0;
}//比较v是否小于w
public static void sort(Comparable[] src){
aux = src.clone();
sort(src, aux,0, src.length-1);
}
private static void sort(Comparable[] src, Comparable[] aux, int lo, int hi) {
//if (hi <= lo) return;
if (hi - lo <= 15) {
Insertion(src, lo, hi);
return;
}
int mid = lo + (hi - lo) / 2;
sort(aux, src, lo, mid);//交换角色
sort(aux, src, mid + 1, hi);
if (less(aux[mid], aux[mid + 1])) {
System.arraycopy(aux, lo, src, lo, hi + 1 - lo);//如果已经有序,只需复制过来就好
return;
}
merge(src, aux, lo, mid, hi);//保持原顺序,确保最终是src被排好序
}
private static void merge(Comparable[] src, Comparable[] aux, int lo, int mid, int hi){
int i = lo;
int j = mid + 1;
// 少了复制到辅助数组的步骤
// for(int k = lo; k <= hi; k++){
// aux[k] = a[k];
// }
for(int k = lo; k <= hi; k++){
if(i > mid) src[k] = aux[j++];
else if(j > hi) src[k] = aux[i++];
else if(less(aux[j],aux[i])) src[k] = aux[j++];
else src[k] = aux[i++];
}
}
private static void Insertion(Comparable[] a, int lo, int hi){
for(int i = lo+1; i <= hi; i++){
for(int j = i; j > lo && less(a[j],a[j-1]); j--){
exch(a,j,j-1);
}
}
}
private static void exch(Comparable[] a, int i, int j){
Comparable t = a[i];
a[i] = a[j];
a[j] = t;
}//元素交换
private static void show(Comparable[] a){
for(int i = 0; i < a.length; i++)
StdOut.print(a[i] + " ");
StdOut.println();
}
public static void main(String[] args){
String[] a = In.readStrings();
sort(a);
show(a);
}
}
src和aux交替角色示意以及分治思想
如图所示,只要第一次sort时顺序正确,最终一定对应merge使得src有正确排序(911,910代表不同地址的两个数组)
Review:
- 避免复制到辅助数组:
前提是sort(Comparable[] src)中执行aux = src.clone()
,aux[]先进行克隆.在重载sort(Comparable[] src,Comparable[] aux, int lo, int hi)中,aux要作为参数传入,交换传参针对两个sort()和arraycopy()
,merge()中aux和src顺序不变. - 检测是否已有序:
若有序,则使用语句System.arraycopy(aux, lo, src, lo, hi+1-lo)
.
该函数对于数组是深拷贝(两个引用指向不同的数组对象),对于数组内的对象是浅拷贝(数组对象中存放的不同对象引用实际指向一个堆上的对象)
.因为操作的传入参数是数组,那么回归本意,效果是深复制.