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是否小于wpublic 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).
该函数对于数组是深拷贝(两个引用指向不同的数组对象),对于数组内的对象是浅拷贝(数组对象中存放的不同对象引用实际指向一个堆上的对象).因为操作的传入参数是数组,那么回归本意,效果是深复制.
