将两个有序的数组归并成一个更大的有序数组。要将一个数组排序,可以先(递归地)将它分成两半分别排序,然后将结果归并起来。
归并排序是算法中分治思想的典型应用
归并排序所需的时间和NlgN成正比。辅助数组所需的额外空间和N的大小成正比。
归并排序是一种渐进最优的基于比较排序的算法。
1. 原地归并
将涉及的所有元素复制到一个辅助数组,再把归并的结果放回原数组。
private static void merge(Comparable[] a,int lo,int mid,int hi)
{ /*将a[lo..mid]和a[mid+1..hi]归并*/
int i=lo;
int j=mid+1;
for(int k=0;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++];
}
}
2.自顶向下的归并排序
归纳证明:如果它能将两个子数组排列,他就能够通过归并两个子数组来将整个数组排序。
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); /*归并结果*/
}
3.自底向上的归并排序
先归并微型数组,然后再成对归并得到子数组
private static Comparable[] aux;
public static void sort(Comparable[] a)
{ /*进行lg次两两归并*/
int N=a.length;
aux=new Comparable[N];
for(int sz=1;sz<N;sz=2*sz) /*sz子数组大小,初始值为1每次加倍*/
for(int lo=0;lo<N-sz;lo=lo+2*sz) /*lo子数组索引*/
merge(a,lo,lo+sz-1,Math.min(lo+2*sz-1,N-1));
}
4.附录
package _2_Sorting;
import java.util.Scanner;
public class Merge
{
/** 算法2.4 自顶向下的归并排序(Top-down mergesort)
* 要对子数组a[lo..hi]进行排序,
* 先将它分为a[lo..mid]和[mid+1..hi]两部分,分别通过递归调用将他们单独排序,
* 最后将有序的子数组归并为最终的排序结果。
*/
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); /*归并结果*/
}
private static void merge(Comparable[] a,int lo,int mid,int hi)
{ /*将a[lo..mid]和a[mid+1..hi]归并*/
int i=lo;
int j=mid+1;
for(int k=0;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++];
}
}
private static boolean less(Comparable v,Comparable w)
{
return v.compareTo(w)<0;
}
private static void exch(Comparable[] a,int i,int j)
{
Comparable t=a[i];
a[i]=a[j];
a[j]=t;
}
private static void show(Comparable[] a)
{
for(int i=0;i<a.length;i++)
System.out.println(a[i]+" ");
System.out.println();
}
private static boolean isSorted(Comparable[] a)
{
for(int i=1;i<a.length;i++)
if(less(a[i],a[i-1]))
return false;
return true;
}
public static void main(String[] args)
{
Scanner sc=new Scanner(System.in);
int N;
N=sc.nextInt();
String[] a=new String[N];
sc.nextLine(); /*读走换行*/
System.out.println("请输入"+N+"个字符串:");
for(int i=0;i<N;i++)
a[i]=sc.nextLine();
sort(a);
show(a);
}
}
package _2_Sorting;
import java.util.Scanner;
/**自底向上的归并排序(Bottom-up mergesort)
* 先进行两两归并、再进行四四归并、然后是八八的归并,一直下去。
* 适合用链表组织的数据
*/
public class MergeBU
{
private static Comparable[] aux;
public static void sort(Comparable[] a)
{ /*进行lg次两两归并*/
int N=a.length;
aux=new Comparable[N];
for(int sz=1;sz<N;sz=2*sz) /*sz子数组大小,初始值为1每次加倍*/
for(int lo=0;lo<N-sz;lo=lo+2*sz) /*lo子数组索引*/
merge(a,lo,lo+sz-1,Math.min(lo+2*sz-1,N-1));
}
private static void merge(Comparable[] a,int lo,int mid,int hi)
{
int i=lo;
int 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[i],aux[j]))
a[k]=aux[i++];
else a[k]=aux[j++];
}
private static boolean less(Comparable v,Comparable w)
{
return v.compareTo(w)<0;
}
private static void exch(Comparable[] a,int i,int j)
{
Comparable t=a[i];
a[i]=a[j];
a[j]=t;
}
public static void show(Comparable[] a)
{
for(int i=0;i<a.length;i++)
System.out.println(a[i]);
System.out.println();
}
private static boolean isSorted(Comparable[] a)
{
for(int i=1;i<a.length;i++)
if(less(a[i],a[i-1]))
return false;
return true;
}
public static void main(String[] args)
{
Scanner sc=new Scanner(System.in);
int N;
N=sc.nextInt();
String[] a=new String[N];
sc.nextLine(); /*读走换行*/
System.out.println("请输入"+N+"个字符串:");
for(int i=0;i<N;i++)
a[i]=sc.nextLine();
sort(a);
show(a);
}
}