《算法(第四版)》2.2.11改进归并排序:加快小数组的排列速度,检测数组是否已经有序以及通过在递归中交换参数来避免数组复制(请参照书中内容阅读以下内容)。
一、加快小数组的排列速度
对应书中“对小规模数组使用插入排序”,实现方法较为简单,将Merge算法中的if(hi < = lo) return; 改为if(hi - lo < 15) {/插入排序这段排序/; return}即可。
二、检测数组是否已经有序
在merge前检测a[mid]>a[mid + 1]即可,这里a[]可能也是aux[],因为a[]和aux[]可以交换角色。
三、通过递归中交换参数来避免数组复制
对应书中“不将元素复制到辅助数组”。merge算法中有一个辅助数组aux[],在归并前需要遍历a[]将a[]复制到aux[],而灵活运用a[]和aux[]可以减少复制的次数,减少merge归并的时间。
在此之前,考虑到在库函数中使用aux[]这样的数组是不妥当的,因为可能会有多个程序同时使用这个类。而将数组aux[]声明为merge()方法的局部变量后,即使是归并很小的数组都要创建一个新的数组,那么创建新数组将成为归并排序运行时间的主要部分。所以可以将aux[]变为sort方法的局部变量,并将它作为参数传递给merge()方法。
观察归并算法,它是将a[lo…mid]和a[mid+1…hi]归并,实质是依据aux[]对序列a[]进行调整。对于当前的归并算法merge,若调整的序列段与上一次merge没有重叠部分则不用将a[]复制到aux[];反之,若调整的序列段与上一次merge有重叠部分,此时merge发生了越级归并,需要交换a[]和aux[]的角色,并且进行复制,复制的目的是保留之前归并的结果,保证局部有序。
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(aux[j], aux[i]))
a[k] = aux[j++];
else
a[k] = aux[i++];
}
下面是书中给出的自顶向下的归并排序的调用轨迹。当前的merge与上一次merge处于同一层级时,不用进行复制操作,无需交换a[]、aux[]的角色(如第5行和第7行),因为两次归并调整的序列段不重叠。当merge发生越级时,需要交换a[]、aux[]的角色,并进行复制操作(如第7行和第8行)。所以对原有程序改进的要点是判断merge是否越级。
sort(a, 0, 15) L1
sort(a, 0, 7) L2
sort(a, 0, 3) L3
sort(a, 0, 1) L4
merge(a, 0, 0, 1) L5
sort(a, 2, 3) L4
merge(a, 2, 2, 3) L5
merge(a, 0, 1, 3) L4
sort(a, 4, 7) L3
sort(a, 4, 5) L4
merge(a, 4, 4, 5) L5
sort(a, 6, 7) L4
merge(a, 6, 6, 7) L5
merge(a, 4, 5, 7) L4
merge(a, 0, 3, 7) L3
...
引入数组mergeLayer[] = {0, 0},在merge操作前mergeLayer会保存当前层数。mergeLayer[1]时当前的层数,mergeLayer[0]是上一次merge操作前所在的层数。当最近两次层数不同时,需要交换a[]和aux[]的角色,并进行复制操作(从第二次merge开始)。merge越级的原因是sort跳入新的层级,所以要保留上一次的lo,mid和hi用于copy操作。改进后的Merge代码如下
public class Merge {
private Comparable[] aux;
private boolean exchange = false; //是否要交换
private int mergeLayer[] = {0, 0}; //merge所在的层级
private int layer = 0; //层级
private Insertion ins = new Insertion(); //对小规模数组使用插入排序
public int copyTimes = 0, mergeTimes = 0, exchangeTimes = 0;
private int twoLo[] = {0, 0}, twoMid[] = {0, 0}, twoHi[] = {0, 1};
public void sort(Comparable[] a) {
aux = new Comparable[a.length];
for(int k = 0; k < a.length; k ++) {
aux[k] = a[k];
}
sort(a, aux, 0, a.length - 1);
}
private void sort(Comparable[] a, Comparable[] aux, int lo, int hi) { //将数组a[lo..hi]排序
// if(hi - lo < 15) { //当长度小于15时先进行插入排序
// ins.sort(a);
// return;
// }
if(hi <= lo)
return;
layer ++;
int mid = lo + (hi - lo)/2; //与a,aux无关,与lo,mid,hi有关
//将序列分为最小单元
sort(a, aux, lo, mid); //将左半边排序
sort(a, aux, mid+1, hi); //将右半边排序
mergeLayer[0] = mergeLayer[1]; mergeLayer[1] = layer;
twoLo[0] = twoLo[1]; twoLo[1] = lo;
twoHi[0] = twoHi[1]; twoHi[1] = hi;
twoMid[0] = twoMid[1]; twoMid[1] = mid;
if(mergeLayer[1] != mergeLayer[0] && mergeLayer[0] != 0) { //从第二次merge开始
exchange = !exchange;
exchangeTimes ++;
copy(a, aux, twoLo[0], twoMid[0], twoHi[0]);
}
if(exchange) {
if(a[mid].compareTo(a[mid + 1]) > 0) {
merge(aux, a, lo, mid, hi);
mergeTimes ++;
}
} else {
if(aux[mid].compareTo(aux[mid + 1]) > 0) {
merge(a, aux, lo, mid, hi);
mergeTimes ++;
}
}
layer --;
}
//当merge越级后,需要对a或aux进行copy
public void copy(Comparable[] a, Comparable[] aux, int lo, int mid, int hi) {
if(a[mid].compareTo(a[mid + 1]) > 0) {
for(int s = lo; s <= hi; s ++)
a[s] = aux[s];
} else if(aux[mid].compareTo(aux[mid + 1]) > 0) {
for(int s = lo; s <= hi; s ++)
aux[s] = a[s];
}
copyTimes ++;
}
public void merge(Comparable[] a, Comparable[] aux, int lo, int mid, int hi) {
//将a[lo..mid]和a[mid+1..hi]归并
//依据aux对序列进行调整
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(aux[j], aux[i]))
a[k] = aux[j++];
else
a[k] = aux[i++];
}
//打印
System.out.print("output:");
for(int t = 0; t < a.length; t ++)
System.out.print(a[t] + " ");
System.out.print(" refer:");
for(int t = 0; t < aux.length; t ++)
System.out.print(aux[t] + " ");
System.out.println();
}
private boolean less(Comparable v, Comparable w) {
return v.compareTo(w) < 0;
}
}
输入的序列为“MERGESORTEXAMPLE优快云”,将每一次merge后的结果打印
output:E E G M R O R S T E X A M P L E C S D N refer:E E G M R O S R T E X A M P L E C S D N
output:E E G M R O R S E T X A M P L E C S D N refer:E E G M R O S R T E X A M P L E C S D N
output:E E G M R E O R S T X A M P L E C S D N refer:E E G M R O R S E T X A M P L E C S D N
output:E E E G M O R R S T X A M P L E C S D N refer:E E G M R E O R S T X A M P L E C S D N
output:E E E G M O R R S T A X M P L E C S D N refer:E E E G M O R R S T X A M P L E C S D N
output:E E E G M O R R S T A M X P L E C S D N refer:E E E G M O R R S T A X M P L E C S D N
output:E E E G M O R R S T A M X L P E C S D N refer:E E E G M O R R S T A X M P L E C S D N
output:E E E G M O R R S T A L M P X E C S D N refer:E E E G M O R R S T A M X L P E C S D N
output:E E E G M O R R S T A L M P X C E S D N refer:E E E G M O R R S T A L M P X E C S D N
output:E E E G M O R R S T A L M P X C D E N S refer:E E E G M O R R S T A L M P X C E S D N
output:E E E G M O R R S T A C D E L M N P S X refer:E E E G M O R R S T A L M P X C D E N S
output:A C D E E E E G L M M N O P R R S S T X refer:E E E G M O R R S T A C D E L M N P S X
A C D E E E E G L M M N O P R R S S T X copyTimes: 14 mergeTimes: 16 exchangeTimes:14
可能的改进:a[]和aux[]是否充分利用,是否无需copy操作,欢迎检验指正。