归并排序详解,与其他排序算法的比较
归并排序
将两个有序的数组归并成一个更大的有序数组
一种简单的方法是:将两个数组归并到第三个数组中(需要额外的空间)
更常见的做法是:避免了开辟额外空间,采用原地归并的方式
原地归并的抽象方法
public static void merge(Comparable[] a, int lo, int mid, int hi){
// 将大数组a分成两个小数组,a[lo, mid], a[mid, hi] 分别进行排序
int j = mid+1;
int i = lo;
// 数组复制,复制到辅助数组中
System.arraycopy(a, lo, aux, lo, hi);
for(int k = lo; k <= hi; k++) {
if (i > mid) // 当左侧小数组为空时
a[k] = aux[j++];
else if (j > hi) // 当右侧小数组为空时
a[k] = aux[i++];
// 比较左右两个数组首位,如果aux[j]小于aux[i]
else if (less(aux[j], aux[i]))
// 将 aux[j] 放入原数组 并索引下移
a[k] = aux[j++];
else // 否则将 aux[i] 放入原数组 并索引下移
a[k] = aux[i++];
}
}
自顶向下的归并排序
算法讲究先从上直接递归到最小数组,将最小数组排序
步骤:
- 通过递归将数组不断二分,直到小数组只含有两个元素为止
- 将只含有两个元素的数组排序后,再重新组成上层数组
- 如此反复,直到大数组为止
package Sort;
import java.util.Arrays;
import java.util.Random;
import static Sort.SortExample.isSorted;
import static Sort.SortExample.less;
public class MergeSort {
public static Comparable[] aux;
// 主函数,供外界调用
public static void sort(Comparable[] b){
Comparable[] a = new Comparable[b.length];
System.arraycopy(b, 0, a, 0, b.length);
// System.out.println(Arrays.toString(b));
aux = new Comparable[a.length]; // 1000
sort(a, 0, a.length-1); // 1000
}
// 通过递归将大数组不断划分,直到每个小数组只有两个元素
public static void sort(Comparable[] a, int lo, int hi){
// 将数组a[lo..hi] 排序
if(hi <= lo)
return;
int mid = lo + (hi-lo)/2; // 4
sort(a, lo, mid); // 左半边排序
sort(a, mid+1, hi); // 右半边排序
merge(a, lo, mid, hi); // 归并结果
}
}
自底向上的归并排序
从最小数组开始排序,再排序较大数组,循环渐进地进行
public class MergeSortBU {
public static void sort(Comparable[] b){
Comparable[] a = new Comparable[b.length];
System.arraycopy(b, 0, a, 0, b.length);
// 进行 lgN 次两两归并
int N = a.length;
Comparable[] aux = new Comparable[N];
for(int sz = 1; sz < N; sz *= 2){ // 以2个元素为一个最小数组
for(int lo = 0; lo < N-sz; lo += sz+sz) // 以相邻的两个小数组为一组进行归并
merge(a, lo, lo+sz-1, Math.min(lo+sz+sz-1, N-1)); // 将相邻的两个小数组归并
if(!isSorted(a))
System.out.println("自底向上归并排序失败");
}
}
}
特点:
- 可以用来实现对链表的排序,只需要重新组织链表连接就能将链表原地排序
注意点:
- 归并排序的空间复杂度不是最优的
- 在实践中不一定会遇到最坏情况
- 除了比较,算法的其他操作(例如:访问数组)也很重要
- 不进行比较也能将某些数据排序
四种排序算法的比较
数据来源:algo4 官方提供的 algo4-data.zip 数据包
先进行1k数据量的比较:
选择排序,1000个数据,执行时间为:0.01177 s
插入排序,1000个数据,执行时间为:0.0137114 s
希尔排序,1000个数据,执行时间为:0.0014275 s
自顶向下归并排序,1000个数据,执行时间为:0.001443 s
自底向上归并排序,1000个数据,执行时间为:3.711E-4 s
进行32k数据量的比较:
选择排序,32000个数据,执行时间为:4.2373938 s
插入排序,32000个数据,执行时间为:2.6435673 s
希尔排序,32000个数据,执行时间为:0.0226827 s
自顶向下归并排序,32000个数据,执行时间为:0.0186592 s
自底向上归并排序,32000个数据,执行时间为:0.0120077 s
其他有关排序算法的文章:
选择排序,插入排序,希尔排序的详解与比较