《算法(第四版)》2.2.11改进归并排序,练习题学习小结

《算法(第四版)》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;
	}
}

输入的序列为“MERGESORTEXAMPLECSDN”,将每一次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操作,欢迎检验指正。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值