public static void sort(Comparable[] a) {
Comparable[] aux = new Comparable[a.length];
sort(a, aux, 0, a.length - 1);
assert isSorted(a);
}
private static void sort(Comparable[] a, Comparable[] aux, int lo, int hi) {
if (hi <= lo)
return;
int mid = lo + (hi - lo) / 2;
sort(a, aux, lo, mid);
sort(a, aux, mid + 1, hi);
merge(a, aux, lo, mid, hi);
}
private static void merge(Comparable[] a, Comparable[] aux, int lo, int mid, int hi) {
assert isSorted(a, lo, mid);
assert isSorted(a, mid + 1, hi);
for (int k = lo; k <= hi; k++) {
aux[k] = a[k];
}
int i = lo, j = mid + 1;
for (int k = lo; k <= hi; k++) {
if (i > mid)
a[k] = aux[j++];
else if (j > hi)
a[k] = aux[i++];
else if (less(aux[j], aux[i]))
a[k] = aux[j++];
else
a[k] = aux[i++];
}
assert isSorted(a, lo, hi);
}
private static boolean less(Comparable v, Comparable w) {
return v.compareTo(w) < 0;
}
private static boolean isSorted(Comparable[] a) {
return isSorted(a, 0, a.length - 1);
}
private static boolean isSorted(Comparable[] a, int lo, int hi) {
for (int i = lo + 1; i <= hi; i++)
if (less(a[i], a[i - 1]))
return false;
return true;
}
上述时间复杂度为O(nlgn),空间复杂度为O(n),适用于排序大列表,基于分治法。
下列图片来展示长度16数组归并过程
归并结果轨迹 调用轨迹
归并排序有以下几点优化方法:
- 和快速排序一样,对于小数组可以使用插入排序或者选择排序,避免递归调用。
- 在merge()调用之前,可以判断一下a[mid]是否小于等于a[mid+1]。如果是的话那么就不用归并了,数组已经是有序的。原因很简单,既然两个子数组已经有序了,那么a[mid]是第一个子数组的最大值,a[mid+1]是第二个子数组的最小值。当a[mid]<=a[mid+1]时,数组整体有序。
- 为了节省将元素复制到辅助数组作用的时间,可以在递归调用的每个层次交换原始数组与辅助数组的角色。
- 在merge()方法中的归并过程需要判断i和j是否已经越界,即某半边已经用尽。可以用另一种方式,去掉检测是否某半边已经用尽的代码。具体步骤是将数组a[]的后半部分以降序的方式复制到aux[],然后从两端归并。对于数组{1,2,3}和{2,3,5},第一个子数组照常复制,第二个则从后往前复制,最终aux[]中的元素为{1,2,3,5,3,2}。这种方法的缺点是使得归并排序变为不稳定排序。代码实现如下:
void merges(Comparable[] a, Comparable[] aux, int lo, int mid, int hi) {
for (int k = lo; k <= mid; k++) {
aux[k] = a[k];
}
for(int k = mid+1; k <= hi; k++) {
aux[k] = a[hi-j+mid+1];
}
int i = lo, j = hi;
for (int k = lo; k <= hi; k++) {
if (less(aux[j], aux[i])) {
a[k] = aux[j--];
}
else {
a[k] = aux[i++];
}
}
}
下面代码将上述前三种优化结合在一起
public void sort(Comparable[] a) {
Comparable[] aux = a.clone();
//将aux放在第一位
sort(aux,a,0,a.length-1);
assert isSorted(a);
}
private void sort(Comparable[] src, Comparable[] dst, int lo, int hi) {
if (hi <= lo + CUTOFF) {
insertionSort(dst, lo, hi);
return;
}
int mid = (lo + hi)/2;
//对调
sort(dst, src, lo, mid);
sort(dst, src, mid+1, hi);
// if (less(src[mid], src[mid+1])) {
// for (int i = lo; i <= hi; i++) dst[i] = src[i];
// return;
// }
//更快
if (less(src[mid], src[mid+1])) {
System.arraycopy(src, lo, dst, lo, hi-lo+1);
return;
}
//对调
merge(src, dst, lo, mid, hi);
}
private void merge(Comparable[] src, Comparable[] dst, int lo, int mid, int hi) {
assert isSorted(src, lo, mid);
assert isSorted(src, mid+1, hi);
int i = lo, j = mid+1;
for (int k = lo; k <= hi; k++) {
if (i > mid) dst[k] = src[j++];
else if (j > hi) dst[k] = src[i++];
else if (less(src[i], src[j])) dst[k] = src[i++];
else dst[k] = src[j++];
}
assert isSorted(dst, lo, hi);
}
private void insertionSort(Comparable[] a, int lo, int hi) {
for (int i = lo; i <= hi; i++) {
for (int j = i; j>lo && less(a[j], a[j-1]); j--) {
exch(a, j, j-1);
}
}
}
还有一种非递归版本:
public void sort(Comparable[] a) {
int n = a.length;
Comparable[] aux = new Comparable[n];
for (int len = 1; len < n; len *= 2) {
//自底向上的归并排序,先前一个与后一个排,再前两个与后两个,再前四个与后四个...
//要考虑到末尾元素不够的情况
for (int lo = 0; lo < n - len; lo += len * 2) {
int hi = Math.min(lo + len * 2 - 1, n - 1);
int mid = lo + len - 1;
merge(a, aux, lo, mid, hi);
}
}
}
public void merge(Comparable[] a, Comparable[] aux, int lo, int mid, int hi) {
for (int k = lo; k < hi; k++) {
aux[k] = a[k];
}
int i = lo, j = mid + 1;
for (int k = lo; k < hi; k++) {
if (i > mid)
a[k] = aux[j++];
else if (j > hi)
a[k] = aux[i++];
else if (less(aux[j], aux[i]))
a[k] = aux[j++];
else
a[k] = aux[i++];
}
}
1.jpg
网友评论