思想:
归并排序是利用归并的思想实现的排序方法,采用分治策略,将问题分成一些小的问题然后递归求解,而治的阶段则将分的阶段得到的各答案"修补"在一起,即分而治之。
再详细看一下治阶段的详细操作:
比如上图中的最后一次合并,要将[4,5,7,8]和[1,2,3,6]两个已经有序的子序列,合并为最终序列[1,2,3,4,5,6,7,8],是如下图这般的步骤实现的:
这里我们用递归来实现代码,递归深度为log2n:
public class mergeSort<T> {
/**
* 归并排序函数
* @param a 存放最终结果的数组
* @param first 数组第一个元素的下标
* @param mid 数组二分元素的下标
* @param last 数组末尾元素的下标
* @param x 临时数组,来做元素的顺序处理
*/
public static <T extends Comparable<T>> void merge(T[] a, int first, int mid, int last, T[] x) {
int i = first;
int m = mid;
int j = mid + 1;
int n = last;
int temp = 0;//临时数组X的下标值
//将待排序数组分为两段处理,小的在前面
while (i <= m && j <= n) {
if (a[i].compareTo(a[j])<=0)
x[temp++] = a[i++];
else
x[temp++] = a[j++];
}
//处理剩下的元素
while (i <= m) x[temp++] = a[i++];
while (j <= n) x[temp++] = a[j++];
//将临时数组x的值给a数组
for (i = 0; i < temp; i++) a[first + i] = x[i];
}
//递归的把数组划分为两个子数组,一直递归到数组中只有一个元素,开始回溯
static <T extends Comparable<T>> void recursionSort(T a[], int first, int last, T temp[]) {
if (first < last)//设置递归出口
{
int mid = (first + last) / 2;
recursionSort(a, first, mid, temp);//处理左边,使左边子序列有序
recursionSort(a, mid + 1, last, temp);//处理右边,使右边子序列有序
merge(a, first, mid, last, temp);//调用归并排序函数,两个有序数组排序
}
}
public <T extends Comparable<T>> void sort(T[] a) {
int n = a.length;
T[] b = (T[]) new Comparable[n];
recursionSort(a, 0, n - 1, b);
}
/**
* 测试
* @param args
*/
public static void main(String[] args) {
Integer[] a = {4, 9, 8, 35, 7, 39, 36, 3};
mergeSort<Integer> m = new mergeSort<Integer>();
m.sort(a);
for (int i : a) {
System.out.print(i + " ");
}
}
}
测试结果:
3 4 7 8 9 35 36 39
总结:
归并排序是稳定排序,它也是一种十分高效的排序,能利用完全二叉树特性的排序一般性能都不会太差。
java中Arrays.sort()采用了一种名为TimSort的排序算法,就是归并排序的优化版本。从上文的图中可看出,每次合并操作的平均时间复杂度为O(n),而完全二叉树的深度为|log2n|。总的平均时间复杂度为O(nlogn)。而且,归并排序的最好,最坏,平均时间复杂度均为O(nlogn)。