归并排序

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

归并排序:要将一个数组排序,先将它分成两半分别排序,然后将结果归并起来。

优点:能够保证将任意长度为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小)。

posted @ 2018-08-01 13:28  菠菜汤圆  阅读(115)  评论(0编辑  收藏  举报