归并:将两个有序数组归并成一个更大的有序数组。
归并排序:要将一个数组排序,先将它分成两半分别排序,然后将结果归并起来。
优点:能够保证将任意长度为N的数组排序所需的时间和NlgN成正比
缺点:所需额外空间和N成正比
一、原地归并的抽象方法
现将前半部分排序,再将后半部分排序,然后在数组中移动元素而不需要使用额外的空间。
public static void merge(Comparable[] a,int lo,int mid,int hi){ //将a[lo...mid]和a[mid...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++]; } }
方法描述:1、将所有元素复制到数组aux[]中
2、进行归并,在第二个for循环中,若左半部分元素已经用完,取右半部分元素;右半部分元素用完,则取左半部分元素;右半边元素小于左半部分元素时,取右半部分元素;左半边元素小于右半边元素时,取左半边元素。
二、自顶向下并归排序
基于原地并归实现的一种递归并归。
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 -1; int mid = (hi-lo)/2+lo; sort(a,lo,mid);//将数组左半边排序 //将数组右半边排序 sort(a,mid,hi); //此方法代码见一原地归并 merge(a,lo,mid,hi); } }
三、自底向上并归
先并归微型数组,再成对并归得到的子数组,如此反复,知道将整个数组并归到一起。
步骤:1、进行两两并归(把每个元素想象成一个大小为1的数组)
2、四四并归(将两个大小为2的数组并归成一个有4个元素的数组)
......
public class MergeBU{ //并归所需要的辅助数组 private static Comparable[] aux; //merge方法代码见第一部分 public static void sort(Comparable[] a){ //进行lgN次两两并归 int N = a.length; aux = new Comparable[N]; for(int sz = 1; sz<N;sz = sz+sz) for(int lo=0;lo<N-sz;lo+=sz) merge(a,lo,lo+sz-1,Math.min(lo+sz+sz-1,N-1)); } }
自底向上的归并排序会多次遍历整个数组,根据子数组大小进行两两归并。子数组的大小sz的初始值为1,每次加倍。最后一个子数组的大小只有在数组大小是sz的偶数倍的时候才会等于sz(否则它会比sz小)。