思想
(1)尽可能的一组数据拆分成两个元素相等的子组,并对每一个子组继续拆分,直到拆分后的每个子组的元素个数是
1为止。
(2)将相邻的两个子组进行合并成一个有序的大组;
(3)不断的重复步骤2,直到最终只有一个组为止。
API设计
归并的原理
定义一个辅助数组 和两个辅助指针
代码实现
import org.junit.Test;
import java.util.Arrays;
/**
* 归并排序
*/
public class Merge {
/**
* 完成归并操作需要的辅助数组assist
*/
private static Comparable[] assist;
/**
* 对数组a中的元素进行排序
*/
public static void sort(Comparable[] a){
//对辅助数组进行初始化
assist=new Comparable[a.length];
int lo=0;
int hi=a.length-1;
sort(a,lo,hi);
}
/**
* 对数组a中从lo到hi的元素进行排序
*/
private static void sort(Comparable[]a ,int lo,int hi){
//给递归限制临界值
if(lo>=hi){
return;
}
int mid=(lo+hi)/2;
//对左边lo到mid之间的元素进行排序
sort(a,lo,mid);
//对右边mid+1到hi 之间的元素进行排序
sort(a,mid+1,hi);
//将lo~mid 数组 和mid+1 ~ hi 数组进行合并
merge(a,lo,mid,hi);
}
/**
* 对数组中,从lo到mid为一组,从mid+1到hi为一组,对这两组数据进行归并
*/
private static void merge(Comparable[]a,int lo,int mid,int hi){
//定义一个指针,指向assist数组中开始填充数据的索引
int i=lo;
//定义一个指针,指向左边小组数据的第一个元素
int p1=lo;
//定义一个指针,指向右边小组数据的第一个元素
int p2=mid+1;
//比较左边小组和右边小组中的元素大小,哪个小,就把哪个数据填充到assist数组中
while (p1<=mid && p2<=hi){
if(less(a[p1],a[p2])){
assist[i++]=a[p1++];
}else {
assist[i++]=a[p2++];
}
}
/**
* 上面的循环结束,判断结束条件
* 如果是p1>mid,证明左边小组中的数据已经归并完毕
* 如果是p2>hi,证明右边小组中的数据已经归并完毕
*/
//所以需要把未填充完毕的数据继续填充到assist中
// 下面两个循环,只会执行其中的一个
//遍历,如果p1的指针没有走完,那么顺序移动p1指针,把对应的元素放到辅助数组的对应索引处
while (p1<=mid){
assist[i++]=a[p1++];
}
//遍历,如果p2的指针没有走完,那么顺序移动p2指针,把对应的元素放到辅助数组的对应索引处
while (p2<=hi){
assist[i++]=a[p2++];
}
//把辅助数组中的元素拷贝到原数组中
for (int index = lo; index <=hi; index++) {
a[index]=assist[index];
}
}
/**
* 比较v元素是否小于w元素
*/
private static boolean less(Comparable v,Comparable w){
return v.compareTo(w)<0;
}
@Test
public void test(){
Integer[] a={9,8,7,5,4,3,2,1};
sort(a);
System.out.println(Arrays.toString(a));
}
}
归并排序的缺点
需要申请额外的数组空间,导致空间复杂度提升,是典型的以空间换时间的操作。