算法思想
归并排序算法是将两个(或两个以上)有序表合并成一个新的有序表,即把待排序序列分为若干个子序列,每个子序列是有序的。然后再把有序子序列合并为整体有序序列。(采用的是分治法的策略)
实现过程
①. 将序列每相邻两个数字进行归并操作,形成 floor(n/2)个序列,排序后每个序列包含两个元素;
②. 将上述序列再次归并,形成 floor(n/4)个序列,每个序列包含四个元素;
③. 重复步骤②,直到所有元素排序完毕(递归实现)
代码实现
/**
* ClassName:MergeSrot
* Author:LFM
* Date:2019/6/20 16:24
**/
public class MergeSrot {
public static int[] sort(int [] a) {
if (a.length <= 1) {
return a;
}
//右移运算符,>>1相当于除以2
int num = a.length >> 1;
//该方法是将源数组按指定长度赋值到另一个新数组(从下标from开始复制)(不包括结尾元素)
int[] left = Arrays.copyOfRange(a, 0, num);
int[] right = Arrays.copyOfRange(a, num, a.length);
//递归调用,让源数组分成每个数组都只有一个元素为止
int [] lefted = sort(left);
int [] righted = sort(right);
//下面这个操作是要递归至源数组元素剩下一个元素才能执行,传入新数组排好序返回完成一次递归操作
return mergeTwoArray(lefted, righted);
}
public static int[] mergeTwoArray(int[] a, int[] b) {
int i = 0, j = 0, k = 0;
int[] result = new int[a.length + b.length]; // 申请额外空间保存归并之后数据
while (i < a.length && j < b.length) { //选取两个序列中的较小值放入新数组
if (a[i] <= b[j]) {
result[k++] = a[i++];
} else {
result[k++] = b[j++];
}
}
while (i < a.length) { //序列a中多余的元素移入新数组
result[k++] = a[i++];
}
while (j < b.length) {//序列b中多余的元素移入新数组
result[k++] = b[j++];
}
return result;//最后返回排好序的数组
}
public static void main(String[] args) {
int[] b = {3, 1, 5, 4, 6, 2, 0};
System.out.println(Arrays.toString(sort(b)));
}
}
运行结果
这个算法要理解的就是递归那一部分,分治策略就是将原问题分解为大致相等的小问题,把小问题解决再合并成。
所以这个算法其中完成一次递归操作后是回到上一次递归的结尾处。意思就是递归到最深层次(就是左右长度都为1或者说左右数组都之后一个元素之后),完成排序操作后再一层一层出来(出来的过程就是把排好序的小问题合并的过程)。
时间复杂度
可见归并排序是一个稳定排序算法,从效率上看,归并排序可算是排序算法中的”佼佼者”. 假设数组长度为n,那么拆分数组共需logn, 又每步都是一个普通的合并子数组的过程,时间复杂度为O(n), 故其综合时间复杂度为O(nlogn)。另一方面, 归并排序多次递归过程中拆分的子数组需要保存在内存空间, 其空间复杂度为O(n)。 和选择排序一样,归并排序的性能不受输入数据的影响,但表现比选择排序好的多,因为始终都是O(nlogn)的时间复杂度。代价是需要额外的内存空间。