首页 > 代码库 > 算法:归并排序

算法:归并排序

算法:归并排序

写在前面

  归并排序算法是基于一个简单的归并操作:合并两个有序数组来形成一个更大的有序数组。

  提炼归并的思想,归并排序就是将一个无序数组先划分成两个部分(递归地),对其分别排序,然后再进行合并。

  归并排序无论输入情况,时间复杂度为N*LogN,主要的缺点是使用了一个额外的大小为N的空间。

简单的归并操作:

  首先我们思考一下,如何将两个有序数组合并成一个有序数组?

    我们应该想到新建一个的新数组re,然后分别从两个数组的左边开始,依次比较两个数组中的元素,将数较小的元素先排入新数组中。算法可以如下:

    public static void merge(int[] re,int[] A,int[] B)
    {
        re = new int[100]; // 较大即可
        int i=0,j=0;
        int ptr=0;
        while(i<A.length&&j<B.length)
        {
            if(A[i]<=B[j])
                re[ptr++]=A[i++];
            else
                re[ptr++]=B[j++];
        }    
        while(i<A.length)
            re[ptr++]=A[i++];
        while(j<B.length)
            re[ptr++]=B[j++];    
        for(int t=0;t<ptr;t++)
            System.out.println(re[t]);
    }

根据上面的问题,我们这样想,要将一个无序数组排序可以(递归地)先将它分成两半分别排序,然后将结果归并起来。模式大概如下:

技术分享 

这就是我们所说的 归并排序。其实我们理解的重点是划分与归并,这里涉及递归的操作,为了便于理解,我们对代码进行分解并辅以实例分析一下。

  代码分解与分析

划分操作

public static void sort(Comparable[] a) //这里是排序操作的入口
{
        aux= new Comparable[a.length]; //aux是我们定义的一个与a等大的数组!
        sort(a,0,a.length-1);
}
private static void sort(Comparable[] a,int lo,int 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);
}

说明:  

  E A S Y Q U E S T I O N

  E A S Y Q U         //先取左半部分

  E A S            //左半部分取出

  E A

  E

  A

  S

     Y  Q  U

     Y  Q 

     Y

             Q

             U

         E S T I O N 

         ....

  由于篇幅,我们不便补全,但是划分的效果我们可以看出,不断递归的划分,最后每一小段就只有一个元素(只有一个元素,默认有序呗),下来最关键的就是将这一小段一小段进行合并。

合并操作

  我们在这里的操作是原地归并,所谓原地归并就是在一个数组中,主观的将其划分为两个小数组(下文称为左、右数组),利用下标与大小关系进行合并操作。

public static void merge(Comparable[] a,int lo,int mid,int hi) {
        int i=lo,j=mid+1;         // i表示的是左数组的开始下标,j表示的是右数组的开始下标!
        for(int k=lo;k<=hi;k++)   // 我们先将待合并的元素放到一个临时数组aux中!
            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++];          //第二种情况,左>右,放右
        }
    }

说明:

  技术分享

  我们研究此图的情况时,必须结合划分操作的实例图, A E是最先开始合并的,下来 AE 再和S合并。Y 与 Q 后合并,QY与U进行合并....

完整的归并代码  

public class MergeSort {
    private static Comparable aux[];
    
    public static void merge(Comparable[] a,int lo,int mid,int 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[i], aux[j]))
                a[k]=aux[i++];
            else 
                a[k]=aux[j++];
        }
    }
    
    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) {
        if(hi<=lo)
            return;
        int mid = lo+(hi-lo)/2;
        sort(a,lo,mid);
        sort(a,mid+1,hi);
        merge(a, lo, mid, hi);
    }
    
    public static boolean less(Comparable v,Comparable w) {
        return v.compareTo(w)<0;
    }

}

算法:归并排序