排序算法总结(7)--归并排序
一、简介
理解归并排序有两种思路:1、自顶向下化整为零,2、自底向上循序渐进
1、自顶向下:将无序序列平均分成两部分,分别对这两部分排序,然后合并。这样将一个大问题分解成两个小问题。同理,如何对两个小部分进行排序?分别将它们分解为更小的问题。在这个算法中,分解问题很简单,主要是如何合并两个小序列为一个有序序列。
2、自底向上:首先将长度为n的序列分解成n个长度为1的序列,成对的合并它们,成为n/2个长度为2的有序序列。然后再成对合并,直到合并成原来的长度。这里的关键在于合并序列。
所以归并排序的关键在于合并。如何合并两个有序序列呢?给定两个序列A[p,…q]和A[q+1,…r],都是已经排序好的。首先将两个子序列复制,有两个指针i,j,分别指向复制后的两个序列的起始位置。从两个指针指向的元素中选择较小的元素,放入A[p,…,r]中,对应子序列上的指针向后移。直到其中一个序列遍历完毕,将另一个序列剩余的元素全部放入A[p,…,r]。
二、代码实现
2.1 合并
伪代码
megre(array,p,q,r)
n1=q-p+1;
n2=r-q;
letL[0,...,n1] and R[0,...,n2]是两个新的数组
将 array[p,...,q]复制到L,array[q+1,...,r]复制到R
且arrarR和arrayL中最后一个数是Integer.MAX_VALUE
//合并开始
i=0;
j=0;
for k=p to r
if L[i]<=R[j]
array[k]=L[i];
i++;
else
array[k]=R[j];
j++;
在复制两个子序列时,在他们最后分别放上一个无穷大的值作为哨兵,用于简化代码。当指针指向其中一个哨兵时,他不可能是较小的值,除非两个指针都指向哨兵,此时所有元素已经合并完毕。
代码实现
public static void megre(int[] array,int p,int q,int r){
int n1=q-p+1;
int n2=r-q;
int[] arrayL=new int[n1+1];
int[] arrayR=new int[n2+1];
for(int i=0;i<n1;i++){
arrayL[i]=array[p+i];
}
arrayL[n1]= Integer.MAX_VALUE;
for(int i=0;i<n2;i++){
arrayR[i]=array[q+1+i];
}
arrayR[n2]= Integer.MAX_VALUE;
int i=0;
int j=0;
for(int k=p;k<=r;k++){
if (arrayL[i]<=arrayR[j]){
array[k]=arrayL[i];
i++;
}else{
array[k]=arrayR[j];
j++;
}
}
}
2.2 自顶向下的归并排序
public static void topMegreSort(int[] array,int p,int r){
if (p<r){
int q=(p+r)/2;
topMegreSort(array,p,q);
topMegreSort(array,q+1,r);
megre(array,p,q,r);
}
}
2.3 自底向上的归并排序
public static void bottomMegreSort(int [] array){
int n=array.length;
for(int sz=1;sz<n;sz*=2){
for(int i=0;i<n-sz;i+=sz+sz){
megre(array,i,i+sz-1,Math.min(i+sz+sz-1,n-1));
}
}
}
三、递归方程
自顶向下的归并排序应用了递归的思想,如何从递归的算法中分析时间复杂度呢?我们可以使用递归方程或递归式来描述其运行时间,该方程根据在较小输入上的运行时间来描述在规模为n的问题上的总运行时间。然后通过数学工具求解该递归式给出算法的时间复杂度。例如,在归并排序中,假设
如何求解用递归方程表示的时间复杂度?参见http://www.cnblogs.com/python27/archive/2011/12/09/2282486.html
主要介绍了代入法,迭代法,公式法,母函数法,差分方程法。
四、注意事项
1、归并排序的空间复杂度并不是O(1),在合并的过程中,需要辅助空间
2、归并排序直到递归结束才能确定所有元素的最终位置。