先举个非递归归并排序栗子吧!
例:将1,3,7,2,5,9,6排为升序(注:线内的为升序组)
第一趟归并后:1,3 ,2,7 ,5,9 ,6
第二趟归并后:1,2,3,7 ,5,6,9
第三趟归并后:1,2,3,7,5,6,9
一、我们来分析一下第二趟归并(分三步):
1.第二趟归并前:从左至右【每相邻2个元素组成的组】都是升序的,末尾可能存在一个【元素个数小于2的】升序组。
2.第二趟归并时:从左至右每四个元素为一个单元,将每个单元内的两个升序组通过merge函数合并为一个大的升序组。最后剩余三个元素5,9和6,但这只是特例。令所有【四个元素组成的单元】内部归并后,剩余元素的个数为i,则0<=i<=3。我们不妨心里假设:没有剩余元素怎么办?剩余元素为5怎么办?剩余元素为5,9怎么办?剩余元素为5,9,6怎么办?
- 没有剩余:无需操作。
- 剩余为5:将5单独变为一组
- 剩余为5,9:由于5,9为升序的,可以单独变为一组
- 剩余为5,9,6:先将5,9和6归并为5,6,9,再单独变为一组
3.第二趟归并后:从左至右【每相邻4个元素组成的组】都是升序的,末尾可能存在一个【元素个数小于4的】升序组。
二、 发现递推
注意到【第二趟归并后的描述】与【第二趟归并前的描述】类似,可以循环递推。
循环递推最后得到:从左至右每相邻8个元素组成的组都是升序的,末尾可能存在一个元素个数小于8的升序组。即归并结束,整个组升序。
三、归纳
我们可以发现:第j趟归并后可以确定前2^j个元素为升序。
等效于:n个无序元素排成升序,需要⌈ log2(n) ⌉(向上取整) 趟归并。
又每趟归并时间复杂度为O(n),所以归并排序时间复杂度为O( n*log2(n) )。
四、不多BB看代码
下面是图片演示代码和测试数据,复制代码翻去最下面:
样例:
如果对你有帮助,点个攒呗!
import java.util.Scanner;
public class Main {
//一、merge函数:将升序数组array[s,m]和array[m+1,e]合并为升序数组array[s,e]
private static void merge(int[] array,int s,int m,int e) {
int i=s,j=m+1,k=1;
//i指向前面数组某元素,j指向后面数组某元素,k指向返回的result数组某元素
int[] result=new int[e-s+2];
while(i<=m&&j<=e) {
if(array[i]<array[j]) {
result[k]=array[i];
i++;
}else {
result[k]=array[j];
j++;
}
k++;
}
if(i>m) {//右边数组有剩余
for(;j<=e;j++,k++) {
result[k]=array[j];
}
}else {//左边数组有剩余的元素个数
for(;i<=m;i++,k++) {
result[k]=array[i];//
}
}
//将result[1,k-1]的元素复制到array[s,e]
for(k-=1;k>=1;k--) {
array[k+(s-1)]=result[k];
}
}
//二、主函数
public static void main(String[] args) {
Scanner scan = new Scanner(System.in);
while (scan.hasNext()) {//!!输入多组数据!!!
//1.输入
int len=scan.nextInt();//先输入输入数组的长度
int[] array=new int[len+1];
for(int e=1;e<=len;e++) {//e为临时变量
array[e]=scan.nextInt();
}
//2.快速排序
for(int e=1;e<len;e*=2) {//e=1,2,4,8.....
int start=1,middle=e,end=2*e;
for(int i=1;i<=len/(2*e);i++){//标准合并len/(2*e)次
merge(array,start,middle,end);
start+=2*e;
middle+=2*e;
end+=2*e;
}
int rest=len-start+1;//标准合并后剩余元素个数为rest(0<=rest<=2*e-1)
if(rest>e) {//(1)当e+1<=rest<=2*e-1,剩余元素不是有序的,合并为有序的
merge(array,start,middle,len);
} //(2)当0<=rest<=e,残余元素是有序的,不需处理
}
//3.输出
for(int e=1;e<len;e++) {//e为临时变量
System.out.print(array[e]+" ");
}
System.out.println(array[len]);//输出后换行
}
scan.close();
}
}