合并排序算法是用分治策略实现对 n 个元素进行排序的算法。
1. 递归实现的基本思想:将待排序元素分成大小大致相同的两个集合,然后分别对两个子集合进行排序,最终将两个排好序的子集合合并成为所要求的排好序的集合。
递归实现代码:
@SuppressWarnings("rawtypes")
public static void mergeSort(Comparable[] a,int left,int right){
Comparable[] b=new Comparable[a.length];
if(left<right){
int i=(left+right)/2;
mergeSort(a,left,i);
mergeSort(a,i+1,right);
merge(a,b,left,i,right); //调用 数组合并算法
copy(a,b,left,right); //调用数组拷贝算法
}
}
2. 非递归实现的基本思想:mergeSort算法的递归过程是将待排序集合一分为二,直至待排序集合只剩下一个元素为止,然后不断的合并两个排好序的数组段。使用非递归的过程可以消除划分的过程,首先将待排序数组中的元素进行两两配对;然后用合并算法将他们排序,构成 n/2 组长度为 2 的排好序的子数组段;再将长度为 2 子数组段排序成长度为 4 的子数组段;如此反复循环,直至整个数组成序。
1)消除递归过程的mergeSort算法:
public static void mergeSort(Comparable[] a){
Comparable[] b=new Comparable[a.length];
int s=1; //初始时相邻每组长度为1
while(s<a.length){
mergePass(a,b,s);
s+=s;
mergePass(b,a,s);
s+=s;
}
}
2)用于合并排好序的相邻数组段的 mergePass 算法
@SuppressWarnings("rawtypes") //合并大小为 s的相邻成序的数组段
public static void mergePass(Comparable[] a,Comparable[] b,int s){
int i=0;
while(i<=a.length-2*s){ //合并大小为 s 的相邻2段子数组
merge(a,b,i,i+s-1,i+2*s-1); //第一组[i,i+s-1],第二组[i+s,i+2*s-1]
i=i+2*s;
}
if(i+s<a.length){ //其中一个大小不足 s,即元素个数少于 2S
merge(a,b,i,i+s-1,a.length-1);
}else{ //只剩一组元素,直接复制到 b 数组
for(int j=i;j<a.length;j++)
b[j]=a[j];
}
}
3)具体合并算法 merge算法
@SuppressWarnings({ "unchecked", "rawtypes" }) //实现数组段的具体的合并
public static void merge(Comparable[] a,Comparable[] b,int m,int n,int r){
int i=m,j=n+1,k=m;
while((i<=n)&&(j<=r)){
if(a[i].compareTo(a[j])<=0) //调用comparaTo()方法比较元素大小
b[k++]=a[i++];
else
b[k++]=a[j++];
}
if(i>n){ //判断子集合c[n+1,r]没有排完的情况
for(int q=j;q<=r;q++){
b[k++]=a[q];
}
}else{ //判断子集合c[m,n]没有排完的情况
for(int q=i;q<=n;q++){
b[k++]=a[q];
}
}
}
3. 自然合并排序算法:相对于非递归mergeSort算法,事实上给定的初始数组通常存在多个长度大于1 的已成序的子数组段。自然合并的基本思想就是对待排序数组用一次的线性扫描就找出所有排好序的子数组段;然后将相邻的成序子数组段两两合并,构成更大的成序子数组段;最后再使用merge算法合并子数组段。
1)自然排序优化的 mergeSort算法
@SuppressWarnings("rawtypes") //合并相邻成序的数组段
public static void mergePass(Comparable[] a,Comparable[] b,int s,int n){
int i=0;
while(i<=count-2*s){
int r=((i+2*s)<count)?t[i+2*s]:n; //当自然排好序片段为偶数时,就r=n,表示刚好两两合并成功,最后没有多余的子数组段
merge(a,b,t[i],t[i+s]-1,r-1); //第一组[t[i],t[i+s]-1],第二组[t[i+s],r-1]
i=i+2*s;
}
if(i+s<count){ //其中一个大小不足 s,即元素个数少于 2S
merge(a,b,t[i],t[i+s]-1,n-1);
}
else if(i<count){ //只剩一组元素,直接复制到 b 数组
for(int j=t[i];j<a.length;j++)
b[j]=a[j];
}
}
2)优化的合并成序相邻数组段的 mergePass算法
@SuppressWarnings({ "unchecked", "rawtypes" }) //实现整体数组的合并
public static void merge(Comparable[] a,Comparable[] b,int m,int n,int r){
int i=m,j=n+1,k=m;
while((i<=n)&&(j<=r)){
if(a[i].compareTo(a[j])<=0) //调用comparaTo()方法比较元素大小
b[k++]=a[i++];
else
b[k++]=a[j++];
}
if(i>n){ //判断子集合c[n+1,r]没有排完的情况
for(int q=j;q<=r;q++){
b[k++]=a[q];
}
}else{ //判断子集合c[m,n]没有排完的情况
for(int q=i;q<=n;q++){
b[k++]=a[q];
}
}
}
3) 线性扫描待排序数组 a[],获取已成序的子数组段
@SuppressWarnings({ "rawtypes", "unchecked" })
public static void getPoint(Comparable a[]) //线性扫描a[],记录下已排好序的子数组段下标
{
int j=0;
t[j++]=0;
for(int i=0;i<a.length-1;i++){
if(a[i+1].compareTo(a[i])<=0)
t[j++]=i+1;
}
count=j;
for(int i=0;i<t.length;i++){
System.out.print(t[i]+",");
}
System.out.println();
}
算法测试:
public static void main(String[] args) {
// TODO Auto-generated method stub
Comparable[] a={4,8,3,7,9,5,2,6};
System.out.println("排序前:"+Arrays.toString(a));
mergeSort(a,0,a.length-1); //调用排序算法
System.out.println("递归实现合并排序后:"+Arrays.toString(a));
mergeSort(a); //调用排序算法
System.out.println("非递归实现合并排序后:"+Arrays.toString(a));
getPoint(a);
mergeSort(a,a.length); //调用排序算法
System.out.println("自然合并排序后:"+Arrays.toString(a));
}
合并排序测试结果: