算法:归并排序
写在前面
归并排序算法是基于一个简单的归并操作:合并两个有序数组来形成一个更大的有序数组。
提炼归并的思想,归并排序就是将一个无序数组先划分成两个部分(递归地),对其分别排序,然后再进行合并。
归并排序无论输入情况,时间复杂度为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; } }