归并排序

归并排序:所需时间为NlogN,但所需额外空间与N成正比。

原地归并:

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[lo..hi]复制到aux[lo..hi]
		aux[k] = a[k];
	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 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)
	{	//将数组[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的任意数组,自顶向下的归并排序需要0.5NlgN到NlgN次比较。主要缺点是辅助数组所使用的额外空间和N的大小成正比。另外,归并排序在小规模问题中对于递归方法的调用过于频繁,可以使用插入排序处理小规模的子数组(比如长度小于15),再使用归并排序,一般可以将归并排序的运行时间缩短10%到15%。另外,如果a[mid]<=a[mid+1],则可以跳过merge()方法,这样对于任意有序的子数组算法的运行时间就是线性的。

自底向上的归并排序

public class MergeBU
{
	private static Comparable[] aux;           //归并所需的辅助数组
	public static void sort(Comparable[] a)
	{	//进行lgN次两两归并
		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);
	}
}

  当数组长度为2的幂时,两种排序方法所用比较次数和数组访问次数相同,还是顺序不同。

posted @ 2018-05-08 21:33  ppwq  阅读(175)  评论(0)    收藏  举报