import java.util.Arrays;
/**
* 归并排序:基于递归和分治的思想,将一个数组对半分,然后将结果归并起来,被对半的数组又可以递归的对半分和归并... 其核心方法是原地归并,归并的过程就是排序的过程。
* 时间复杂度:对于长度为N的任意数组,归并排序需要 1/2NlogN~NlogN 次比较,最多访问数组 6NlogN 次。
* 特点:能够将任意长度为N的数组排序所需时间降低到与NlogN成正比,主要缺点是所需的额外空间和N成正比。
* 理论:归并排序是一种渐进最优的基于比较排序的算法。
*/
public class MergeSort {
private Comparable[] toBeSortedArray; // 待排序的数组
private Comparable[] aux; // 归并所需的辅助数组
MergeSort(Comparable[] toBeSortedArray) {
this.toBeSortedArray = toBeSortedArray;
}
// 判断数组是否有序
public boolean isSorted() {
for (int i = 1; i < toBeSortedArray.length; i++)
if (!less(toBeSortedArray[i-1],toBeSortedArray[i]))
return false;
return true;
}
// 判断 a 是否小于 b
private boolean less(Comparable a, Comparable b) {
return a.compareTo(b) < 0;
}
// 将两个有序的子数组合并(一个数组的前后两个部分),并不要求是大小相同的数组,注意传入的是数组的下标
private void merge(int lo, int mid, int hi) {
int i = lo;
int j = mid + 1; // 定义两个扫描指针,分别指向两个子数组开始位置
// 每次归并都要初始化辅助数组,将 toBeSortArray[lo~hi] 复制到 aux[lo~hi]
for (int k = lo; k <= hi; k++)
aux[k] = toBeSortedArray[k];
// 遍历 lo~hi,分四种情况归并到 toBeSortedArray[lo~hi]
for (int k = lo; k <= hi; k++) {
if (i > mid) toBeSortedArray[k] = aux[j++]; // 前一个子数组已经遍历完,存后面的
else if (j > hi) toBeSortedArray[k] = aux[i++]; // 后一个子数组已经遍历完,存前面的
else if (less(aux[i],aux[j])) toBeSortedArray[k] = aux[i++]; // 都没遍历完存小的
else toBeSortedArray[k] = aux[j++];
}
}
// 1. 自顶向下的归并排序:从一个整体开始对半分,直到分到不能分再归并
public void sortTopDown() {
int N = toBeSortedArray.length;
aux = new Comparable[N]; // 懒惰初始化
sortTopDown(0,N-1);
}
// 将数组 toBeSortedArray[lo~hi] 递归排序,注意传入的是数组下标
private void sortTopDown(int lo, int hi) {
if (hi <= lo) return; // 递归返回条件
int mid = lo + (hi-lo)/2;
sortTopDown(lo, mid); // 左半边排序
sortTopDown(mid+1, hi); // 右半边排序
merge(lo, mid, hi); // 归并结果
}
// 2. 自底向上的归并排序:从单个元素开始两两归并,直至归并成一个整体
public void sortBottomUp() {
int N = toBeSortedArray.length;
aux = new Comparable[N]; // 懒惰初始化
// 进行 logN 次两两归并
for (int sz = 1; sz < N; sz*=2) { // 注意这里 sz < N 不能写成 sz < N/2
for (int lo = 0; lo < N-sz; lo += 2*sz)
merge(lo,lo+sz-1,Math.min(lo+2*sz-1,N-1)); // 注意这里的索引,很容易写错
}
}
}
class Test {
public static void main(String[] args) {
// test top-down
Integer[] testArrayTD = {5,8,4,1,2,3,7,9,6,0};
MergeSort mergeSortTD = new MergeSort(testArrayTD);
mergeSortTD.sortTopDown();
System.out.println(mergeSortTD.isSorted());
System.out.println(Arrays.toString(testArrayTD));
// test bottom-up
Integer[] testArrayBU = {5,8,4,1,2,3,7,9,6,0};
MergeSort mergeSortBU = new MergeSort(testArrayBU);
mergeSortBU.sortBottomUp();
System.out.println(mergeSortBU.isSorted());
System.out.println(Arrays.toString(testArrayBU));
}
/*
true
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
true
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
*/
}
2-排序之归并排序
最新推荐文章于 2023-05-14 13:56:12 发布