自顶向下
归并排序分为两步:划分和归并
划分
排序思路:通过递归调用不断地将原始数组进行左右划分,直到划分成只有一个元素时停止,然后return,然后开始合并,其实排序的实现还是在merge里面实现的
public void sort(int [] a)
{
aux=new int[a.length];//一次性分配空间
sort(a,0,a.length-1);//
}
public void sort(int a [],int low,int high)//排序
{
for(int i=low;i<=high;i++)
System.out.print(a[i]+" ");
System.out.println();
if(high<=low)//划分得每个子数组只有一个元素了
return;
int mid=low+(high-low)/2;//再次确定划分得界限
sort(a,low,mid);//将左半部分排序
sort(a,mid+1,high);//将右边部分排序
merge(a,low,mid,high);//开始合并
System.out.print("merge:");
for(int i=low;i<=high;i++)
System.out.print(""+a[i]+" ");
System.out.println();
}
合并
进行合并的两个子数组本身已经是有序的了,在合并是采用一个额外的辅助数组先复制原数组的值,然后进行归并。先设置两个下标i,j,i指向左部第一个元素,j指向右部第一个元素,刚开始时第一个if和第二个if语句一般不满足条件,所以执行的是后面的两个if语句,以数据7 8 9 ‘ 1 2 3为例,右部的元素一直小于左部元素,因此前面取得就是 1 2 3,当右边的元素取完了,此时j>hi,所以第2个if语句条件满足,然后直接取左部元素7 8 9,最后的得到1 2 3 7 8 9。
public void merge(int a[],int low,int mid,int high)//归并
{
int i=low;
int j=mid+1;
for(int k=low;k<=high;k++)
aux[k]=a[k];
for(int k=low;k<=high;k++)
{
if(i>mid) //左边已经用尽,开始取右半边元素
a[k]=aux[j++];
else if(j>high)//右边已经用尽,开始取左半边元素
a[k]=aux[i++];
else if(aux[j]<aux[i])//右半边元素小于左半边元素,取右半边元素---升序
a[k]=aux[j++];
else //左半边元素小于右半边元素,取左半边元素---升序
a[k]=aux[i++];
}
}
从运行结果中可以看出,刚开始一直划分,直到划分到单个元素 16 15时停止,然后归并成15 16,然后再将14 13划分成单个元素14 13,然后归并成13 14,然后再归并成13 14 15 16,接着同样的步骤处理12 11 10 9…
自底向上
不再像自顶向下那样逐次划分,直接对长度为1 2 4 8 …的子数组进行合并
public void sort_from_bottom_to_top(int a[])
{
int N=a.length;
aux=new int[a.length];//一次性分配空间
for(int sz=1;sz<N;sz=sz+sz)//sz为子数组的大小
for(int lo=0;lo<N-sz;lo+=sz+sz)//lo为子数组索引
merge(a,lo,lo+sz-1,Math.min(lo+sz+sz-1,N-1));
}
自底向上的归并排序会多次遍历整个数组,根据子数组的大小进行两两归并。子数组的大小sz的初始值为1,每次加倍。最后一个子数组的大小只有在数组的大小是sz的整数倍的时候才会等于sz,否则它会比sz小。
完整代码
public class test{
int [] aux;//归并时所需的辅助数组
public void sort(int [] a)
{
aux=new int[a.length];//一次性分配空间
sort_from_top_to_bottom(a,0,a.length-1);//
}
public void sort_from_top_to_bottom(int a [],int low,int high)//排序
{
for(int i=low;i<=high;i++)
System.out.print(a[i]+" ");
System.out.println();
if(high<=low)//划分得每个子数组只有一个元素了
return;
int mid=low+(high-low)/2;//再次确定划分得界限
sort_from_top_to_bottom(a,low,mid);//将左半部分排序
sort_from_top_to_bottom(a,mid+1,high);//将右边部分排序
merge(a,low,mid,high);//开始合并
System.out.print("merge:");
for(int i=low;i<=high;i++)
System.out.print(""+a[i]+" ");
System.out.println();
}
public void merge(int a[],int low,int mid,int high)//归并
{
int i=low;
int j=mid+1;
for(int k=low;k<=high;k++)
aux[k]=a[k];
for(int k=low;k<=high;k++)
{
if(i>mid) //左边已经用尽,开始取右半边元素
a[k]=aux[j++];
else if(j>high)//右边已经用尽,开始取左半边元素
a[k]=aux[i++];
else if(aux[j]<aux[i])//右半边元素小于左半边元素,取右半边元素---升序
a[k]=aux[j++];
else //左半边元素小于右半边元素,取左半边元素---升序
a[k]=aux[i++];
}
}
public void sort_from_bottom_to_top(int a[])//自底向上
{
int N=a.length;
aux=new int[a.length];//一次性分配空间
for(int sz=1;sz<N;sz=sz+sz)//sz为子数组的大小
for(int lo=0;lo<N-sz;lo+=sz+sz)//lo为子数组索引
{
merge(a,lo,lo+sz-1,Math.min(lo+sz+sz-1,N-1));
for(int k=lo;k<=Math.min(lo+sz+sz-1,N-1);k++)
System.out.print(a[k]+" ");
System.out.println();
}
}
public static void main(String [] args)
{
int [] a={16,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1};
test t=new test();
System.out.println("初始数组:");
for(int i=0;i<16;i++)
System.out.print(a[i]+" ");
System.out.println();
System.out.println("自顶向下实现:");
t.sort(a);
System.out.println("自底向上实现:");
t.sort_from_bottom_to_top(a);
for(int i=0;i<16;i++)
System.out.print(a[i]+" ");
}
}