归并排序(MERGE-SORT) 是利用归并的思想实现的排序方法,该算法采用经典的分治策略(分治法将问题分(divide)成一些小的问题然后递归求解,而治(conquer)的阶段则将分的阶段得到的各答案"修补"在一起,即分而治之)。
归并排序的原理: 假设初始序列合并n个记录,则可以看成是n个有序子序列,每个子序列的长度为1,然后两两归并……如此重复,直至得到一个长度为m的有序序列为止、这种排序的方法称为2路归并排序。
算法描述: 把长度为n的输入序列分成两个长度为n/2的子序列,对这两个子序列分别采用归并排序,将两个排序好的子序列合并成一个最终的排序序列。
可以看到这种结构很像一棵完全二叉树,本文的归并排序我们采用递归去实现(也可采用迭代的方式去实现)。分阶段可以理解为就是递归拆分子序列的过程。
就上边的图,我们来看下实现过程
步骤(1):
我们初始化时让low为序列的首元素下标,mid=low+arr.length/2,high=mid+1,tmp为辅助序列。
步骤(2):
比较low和high所指的元素,谁小谁就插入到tmp序列中,然后谁再向右走一个单位。如下图所示,4<5,将4插入到tmp中,然后low向右走一个单位,然后5<6,再将5插入tmp中,high向右走一个单位。
步骤(3):
接着继续(2)步骤,一直到将无序序列中的元素全部插入到tmp中为止,这样tmp就是一个有序的序列了
步骤(4)
在得到有序序列tmp后我们将tmp中的数据拷贝到原序列arr中,这样就将原序列arr变成了一个有序序列。
public static int[] sort(int[] a,int low,int high){
int mid = (low+high)/2;
if(low<high){
sort(a,low,mid);
sort(a,mid+1,high);
//左右归并
merge(a,low,mid,high);
}
return a;
}
public static void merge(int[] a, int low, int mid, int high) {
int[] temp = new int[high-low+1];
int i= low;
int j = mid+1;
int k=0;
// 把较小的数先移到新数组中
while(i<=mid && j<=high){
if(a[i]<a[j]){
temp[k++] = a[i++];
}else{
temp[k++] = a[j++];
}
}
// 把左边剩余的数移入数组
while(i<=mid){
temp[k++] = a[i++];
}
// 把右边边剩余的数移入数组
while(j<=high){
temp[k++] = a[j++];
}
// 把新数组中的数覆盖a数组
for(int x=0;x<temp.length;x++){
a[x+low] = temp[x];
}
}
算法分析:
(1)稳定性
归并排序是一种稳定的排序。
(2)存储结构要求
可用顺序存储结构。也易于在链表上实现。
(3)时间复杂度
对长度为n的文件,需进行趟二路归并,每趟归并的时间为O(n),故其时间复杂度无论是在最好情况下还是在最坏情况下均是O(nlgn)。
(4)空间复杂度
需要一个辅助向量来暂存两有序子文件归并的结果,故其辅助空间复杂度为O(n),显然它不是就地排序。