学习排序算法5--归并排序

此博客为连载博客偷笑需要参考我前面几篇博客代码才可以实现

/*归并排序
 * 思想:	divide and conquer (分治法)
 * 		将一个数组等分成两个更小的数组,将这两个数组排序后,再将它们归并起来将得到一个有序数组
 * 		具体归并方法就是从两个数组中从小到大放入大数组中
 * 时间复杂度:  对于长度为N的任意数组,自顶向下的归并排序需要1/2N㏒N至N㏒N	      	O(NlogN)
 * 空间复杂度:需要额外为N的空间											O(N)
 * 特点:运行时间比插入排序等比较大大减少,但辅助数组所使用的额外空间和N的大小成正比
 * */
package paixu;

//自顶向下的归并排序
public class Merge extends Example{
	private static Comparable[] aux;		//归并排序所用的辅助数组
	
	public static void sort(Comparable[] a){
		aux = new Comparable[a.length];		//一次性分配空间
		sort(a,0,a.length-1);
	}
	public static void sort(Comparable[] a,int lo,int hi){
		//将a[lo..hi]排序
		if(hi <= lo)	return;
		int mid = lo + (hi-lo)/2;
		sort(a,lo,mid);				//将左半边排序
		sort(a, mid+1, hi);			//将右半边排序
		merge(a,lo,mid,hi);            //归并结果
	}
	
	public  static void Merge(Comparable[] a,int lo,int mid,int hi) {
		//归并:将a[lo..mid]和a[mid+1..hi]排序
		int i = lo, j = mid+1;
		
		for(int k=lo; k<=hi; k++){
			aux[k] = a[k];			//将a[lo..hi]复制到aux[lo..hi]中
		}
		
		for(int k=lo; k<=hi; k++){	//归并回到a[lo..hi]
			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++];
		}
	}
	public static void main(String[] args) {
		String alg1 = "Shell";
		String alg2 = "Merge";
		int N = 100000;
		int T = 10;
		long t1 = SortCompare.timeRandomInput(alg1, N, T);
		long t2 = SortCompare.timeRandomInput(alg2, N, T);
		System.out.println("希尔排序运行时间:"+t1);
		System.out.println("归并排序运行时间:"+t2);

	}
}

/*归并排序的改进:
 * 1.对小规模数组使用插入排序
 * 2.测试数组是否有序:可以加一个判断条件,如果a[mid]小于a[mid+1],我们就认为数组已经有序并跳过merge方法
 * 3.不要每次都将元素复制到辅助数组,一次排序将输入数组排序到辅助数组,一次将辅助数组排序到输入数组*/

运行结果如下所示:


可以看到,将10个大小为十万的数组排序时,归并排序比希尔排序要快,更不用说选择排序、插入排序了。但归并排序的一个不足是需要O(N)的额外空间。


下面代码我仅仅实现了改进1和2

而且仅仅修改了上述public static void sort(Comparable[] a,int lo,int hi)函数


public static void sort(Comparable[] a,int lo,int hi){
		//将a[lo..hi]排序
		if(hi-lo<15){//对小规模数组使用插入排序
			 for(int i=1; i<hi-lo+1; i++){  
		            Comparable tmp = a[i];  
		            int j;  
		            for( j=i-1; j>=0; j--){  
		                if( less(tmp, a[j]) ){  
		                    a[j+1] = a[j];  
		                }else{  
		                    break;  
		                }  
		            }  
		            a[j+1] = tmp;      
		        }  
			return;
		}
		int mid = lo + (hi-lo)/2;
		sort(a,lo,mid);				//将左半边排序
		sort(a, mid+1, hi);			//将右半边排序
		if(less(a[mid],a[mid+1]))	return;	//如果a[mid]小于a[mid+1],我们就认为数组已经有序并跳过merge方法
		merge(a,lo,mid,hi);			//归并结果
	}
再次运行:

说明这两个改进作用还是挺大的。



/*自底向上的递归排序
 * 思想:任何一种分治法算法,都有自顶向下(递归)和自底向上(循环、迭代)的两种实现方法。
 * 		下面代码是先归并最小的数组,然后归并得到的子数组,如此这般,直到将整个数组归并在一起
 * */
package paixu;

public class MergeBU extends Example{
	private static Comparable[] aux;			//归并排序所需的辅助数组
	public  static void sort(Comparable[] a){
		int N = a.length;
		aux = new Comparable[N];
		for(int sz=1; sz<N; sz*=2){			//子数组大小sz
			for(int lo=0; lo<N-sz; lo+=2*sz){	//lo子数组索引
				merge(a, lo, lo+sz-1, Math.min(N-1, lo+sz+sz-1));
			}
		}
	}
	
	public static void merge(Comparable[] a,int lo,int mid,int hi){
		//将a[lo..mid]和a[mid+1..hi]归并
		int i=lo,j=mid+1;
		for(int k=lo; k<=hi; k++){
			aux[k] = a[k];
		}
		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++];
		}
	}

	public static void main(String[] args) {
		/*Integer[] a = {4,5,6,1,2,3,9,8,7,123,-1,22};
		MergeBU.sort(a);
		MergeBU.show(a);*/
		String alg1 = "Merge";
		String alg2 = "MergeBU";
		int N = 100000;
		int T = 10;
		long t1 = SortCompare.timeRandomInput(alg1, N, T);
		long t2 = SortCompare.timeRandomInput(alg2, N, T);
		System.out.println("自顶向下归并排序运行时间:"+t1);
		System.out.println("自底向上归并排序运行时间:"+t2);
		
	}

}

运行结果如下:


两种方法运行时间基本相同。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值