算法——归并排序

归并:将两个有序的数组归并成一个更大的有序数组。

归并排序(分治思想):要将一个数组排序,可以先(递归地)将它分别分成两半分别排序,然后将结果归并起来。

优点:它能够保证将任意长度为N的数组排序所需时间和NlogN成正比,相对于初级排序,时间大大缩短;

缺点:它所需要的额外空间和N成正比。

 

原地归并的实现:先将所有元素复制到另一额数组(aux[])中,然后在归并回原数组(a[])中,具体归并是循环比较两个有序数组的最左侧元素即最小值,哪个更小,即将这个元素复制回原数组(a[])。

实现代码如下:

	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++) {//将数组a复制到数组aux
			aux[k]=a[k];
		}
		
		for(int k=lo;k<=hi;k++)//遍历整个aux,归并回数组a
			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 class Merge{
	private static Comparable[] aux;//归并所需的辅助数组
	
	public static void sort(Comparable[] a) {
		aux=new Comparable[a.length];//一次性分配空间
		sort(a,0,a.length-1);
	}
	
	private 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);
	}
}

性能:对于长度为N的任意数组,自顶向下的归并需要1/2NlgN至NlgN次比较;最多需要访问数组6NlgN。

改进:①对小规模子数组使用插入排序——是用插入排序处理小规模的子数组(比如长度小于15)一般可以将归并排序的巡行时间缩短10%~15%;

           ②测试数组是否已经有序——添加判断条件,如果a[mid]小于等于a[mid+1],我们就认为数组已经有序而不需要在进行归并即不需要在调用merge()方法,因此可以将任意有序的子数组算法运行时间变为线性;

           ③不将元素复制到辅助数组——可以节省复制到用于归并的辅助数组所用的时间(但空间不可以)。一是通过将数据从输入数组排序到辅助数组;二是将数据从辅助数组排序到输入数组。

自底向上的归并方法

public class MergeBU{
	private static Comparable[] aux;//归并所需的辅助数组
	
	public static void sort(Comparable[] a) {
		//进行lgN次两两归并
		int N=a.length;
		aux=new Comparable[N];
		for(int sz=1;sz<N;sz=sz+sz)//sz子数组大小
			for(int lo=0;lo<N-sz;lo+=sz+sz)//lo:子数组索引
				merge(a,lo,lo+sz-1,Math.min(lo+sz+sz-1, N-1));
	}
}

自底向上的归并排序会多次遍历整个数组,根据子数组大小进行两两归并。子数组的大小sz的初始值为1,每次加倍。最后一个子数组的大小只有在数组大小是sz的偶数倍的时候才会等于sz(否则它会比sz小)。

性能:对于长度为N的任意数组,自底向上的归并排序需要1/2NlgN至NlgN次比较,最多访问数组6NlgN次。

当数组长度为2的幂时,自顶向下和自底向上的归并排序所用的比较次数和数组访问次数相同,只是顺序不同。

自底向上的归并排序比较适合用链表组织的数据。

没有任何基于比较的算法能够保证使用少于lg(N!)~NlgN次比较将长度为N的数组排序。

归并排序是一种渐进最优的基于比较排序的算法。

转载于:https://my.oschina.net/u/3786691/blog/1628919

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值