算法_归并排序

  归并排序的基本思想是:将两个已经有序的数组,归并成为更大的有序数组.这种操作称为归并排序.要让一个数组排序,可以先递归的把它分成两半分别排序,然后将结果归并起来.归并排序能够保证对一个任意长度为N的数组排序所需时间和NlogN成正比.

  基本的归并方法代码如下:该方法先将所有的元素复制到aux[]中,然后再归并到a[]中.方法在归并的时候,进行了四次条件判断:左半边用尽(取右半边的元素),右半边用尽(取左半边的元素),右半边的当前元素小于左半边的当前元素(取右半边的元素)以及右半边的元素大于等于左半边的当前元素.

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>lo) a[k]=aux[i++];
            else if(less(aux[i],aux[j])) a[k]=aux[i++];
            else a[k]=aux[j++];
        }
    }

  利用上面的归并代码,可以产生两种排序方法,自顶向下和自底向上的排序算法,分别整理如下:

  自顶向下的算法认为:如果能够将两个子数组排序,就能够通过归并两个子数组来将整个数组进行排序:

public class Merge {
    private static Comparable[] aux;
    public static void sort(Comparable[] a) {
        aux=new Comparable[a.length];
        sort(a,0,a.length-1);
    }
    public static void sort(Comparable[] a, int lo, int hi) {
        if(hi<=lo) return;
        int mid=lo+(hi-lo)/2;
        sort(a,lo,mid);            //左半边排序[[9
        sort(a,mid+1,hi);        //右半边排序.
        merge(a,lo,mid,hi);    //归并
    }
    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>lo) a[k]=aux[i++];
            else if(less(aux[i],aux[j])) a[k]=aux[i++];
            else a[k]=aux[j++];
        }
    }
    public static boolean less(Comparable v, Comparable w) {
        return v.compareTo(w)<0;
    }
}

  自顶向下的归并排序对于长度为N的任意数组,需要1/2NlgN~NlgN次比较.(证明方法见算法第四版).此外归并排序还有可以优化的地方,如对于小规模的数组可以使用插入排序,可以添加判断条件,例如通过测试a[mid]小于等于a[mid+1],我们就认为数组已经是有序的并跳过merge方法.

  自底向上的归并排序利用先归并那些微型数组,然后再成对归并得到的子数组,如此这般,直到我们将整个数组归并在一起.自底向上的归并排序算法的实现如下:

public class MergeBU {
    private static Comparable[] aux;
    
    public static void sort(Comparable[] a) {
        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+sz) /*lo:子数组的索引*/{
                merge(a,lo,lo+sz-1,Math.min(lo+sz+sz-1, N));
            }
        }
    }
    public static boolean less(Comparable v, Comparable w) {
        return v.compareTo(w)<0;
    }
    public 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>lo) a[k]=aux[i++];
            else if(less(aux[i],aux[j])) a[k]=aux[i++];
            else a[k]=aux[j++];
        }
    }
}

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

原文地址:https://www.cnblogs.com/hlhdidi/p/5638972.html