7、归并排序
1、归并排序
/**
* 归并排序: O(N * logN)
*/
public class MergeSort {
private MergeSort() {
}
/**
* 归并排序
*/
public static <E extends Comparable<E>> void sort(E[] arr) {
sort(arr, 0, arr.length - 1);
}
/**
* 归并排序 arr[l, r]
*/
private static <E extends Comparable<E>> void sort(E[] arr, int l, int r) {
if (l >= r) return;
int mid = l + (r - l) / 2;
sort(arr, l, mid); // arr[l, mid]
sort(arr, mid + 1, r); // arr[mid + 1, r]
merge(arr, l, mid, r);
}
/**
* 合并两个有序数组 arr[l, mid] 和 arr[mid + 1, r], 使得 arr[l, r] 整体有序
*/
private static <E extends Comparable<E>> void merge(E[] arr, int l, int mid, int r) {
E[] temp = Arrays.copyOfRange(arr, l, r + 1);
int p1 = 0; // temp[0, mid - l]
int p2 = mid + 1 - l; // temp[mid + 1 - l, r - l]
int i = l; // arr[l, r]
while (p1 <= mid - l && p2 <= r - l) arr[i++] = temp[p1].compareTo(temp[p2]) <= 0 ? temp[p1++] : temp[p2++];
while (p1 <= mid - l) arr[i++] = temp[p1++];
while (p2 <= r - l) arr[i++] = temp[p2++];
}
}
2、归并排序优化
/**
* 归并排序: O(N * logN)
* 优化 1: merge 条件, 对完全有序的数组 O(n)
* 优化 2: 数据量小的时候(<=16)采用插入排序
* 优化 3: 避免频繁的在内存中开辟空间
*/
public class MergeSortPlus {
private MergeSortPlus() {
}
/**
* 归并排序
*/
public static <E extends Comparable<E>> void sort(E[] arr) {
E[] temp = (E[]) new Comparable[arr.length]; // 优化 3: 避免频繁的在内存中开辟空间
sort(arr, 0, arr.length - 1, temp);
}
/**
* 归并排序 arr[l, r]
*/
private static <E extends Comparable<E>> void sort(E[] arr, int l, int r, E[] temp) {
if (r - l <= 15) {
InsertionSort.sort(arr, l, r); // 优化 2: 数据量小的时候(<=16)采用插入排序
return;
}
int mid = l + (r - l) / 2;
sort(arr, l, mid, temp); // arr[l, mid]
sort(arr, mid + 1, r, temp); // arr[mid + 1, r]
if (arr[mid].compareTo(arr[mid + 1]) > 0) merge(arr, l, mid, r, temp); // 优化 1: merge 条件
}
/**
* 合并两个有序数组 arr[l, mid] 和 arr[mid + 1, r], 使得 arr[l, r] 整体有序
*/
private static <E extends Comparable<E>> void merge(E[] arr, int l, int mid, int r, E[] temp) {
System.arraycopy(arr, l, temp, l, r - l + 1);
int p1 = l; // temp[l, mid]
int p2 = mid + 1; // temp[mid + 1, r]
int i = l; // arr[l, r]
while (p1 <= mid && p2 <= r) arr[i++] = temp[p1].compareTo(temp[p2]) <= 0 ? temp[p1++] : temp[p2++];
while (p1 <= mid) arr[i++] = temp[p1++];
while (p2 <= r) arr[i++] = temp[p2++];
}
}
3、自底向上归并排序
/**
* 自底向上归并排序: O(N * logN)
*/
public class MergeSortBU {
private MergeSortBU() {
}
/**
* 自底向上归并排序
*/
public static <E extends Comparable<E>> void sort(E[] arr) {
int n = arr.length;
E[] temp = (E[]) new Comparable[n];
// 对 arr[0, n - 1] 中所有的 arr[i, i + 15], 使用插入排序法
for (int i = 0; i < n; i += 16) {
InsertionSort.sort(arr, i, Math.min(i + 15, n - 1));
}
// 遍历合并的区间长度(把 2 个区间合并成 1 个区间)
for (int size = 16; size < n; size += size) {
// 合并 arr[i, i + size - 1] 和 arr[i + size, i + size + size - 1]
for (int i = 0; i + size < n; i += 2 * size) {
if (arr[i + size - 1].compareTo(arr[i + size]) > 0) {
merge(arr, i, i + size - 1, Math.min(i + size + size - 1, n - 1), temp);
}
}
}
}
/**
* 合并两个有序数组 arr[l, mid] 和 arr[mid + 1, r], 使得 arr[l, r] 整体有序
*/
private static <E extends Comparable<E>> void merge(E[] arr, int l, int mid, int r, E[] temp) {
System.arraycopy(arr, l, temp, l, r - l + 1);
int p1 = l; // temp[l, mid]
int p2 = mid + 1; // temp[mid + 1, r]
int i = l; // arr[l, r]
while (p1 <= mid && p2 <= r) arr[i++] = temp[p1].compareTo(temp[p2]) <= 0 ? temp[p1++] : temp[p2++];
while (p1 <= mid) arr[i++] = temp[p1++];
while (p2 <= r) arr[i++] = temp[p2++];
}
}
4、复杂度分析
元素个数为 N,那么:节点个数为 2N - 1,层数为 logN + 1
5、逆序数对
public class ReversePairs {
/**
* 归并解法
*/
public static int reversePairs(int[] nums) {
return sort(nums);
}
private static int sort(int[] arr) {
int[] temp = new int[arr.length];
return process(arr, 0, arr.length - 1, temp);
}
private static int process(int[] arr, int l, int r, int[] temp) {
// if (l >= r) return 0;
if (r - l <= 15) return insertionSort(arr, l, r);
int mid = l + (r - l) / 2;
int left = process(arr, l, mid, temp);
int right = process(arr, mid + 1, r, temp);
if (arr[mid] > arr[mid + 1]) return left + right + merge(arr, l, mid, r, temp);
return left + right;
}
private static int merge(int[] arr, int l, int mid, int r, int[] temp) {
int res = 0;
System.arraycopy(arr, l, temp, l, r - l + 1);
int p1 = l;
int p2 = mid + 1;
int i = l;
while (p1 <= mid && p2 <= r) {
if (temp[p1] > temp[p2]) {
// 输出逆序数对
// for (int j = p1; j <= mid; j++) {
// System.out.println(String.format("[%d, %d]", temp[j], temp[p2]));
// }
res += mid - p1 + 1; // 后面区间的元素归并上来时, 和前面区间剩余元素形成逆序数对
arr[i++] = temp[p2++];
} else {
arr[i++] = temp[p1++];
}
}
while (p1 <= mid) arr[i++] = temp[p1++];
while (p2 <= r) arr[i++] = temp[p2++];
return res;
}
private static int insertionSort(int[] arr, int l, int r) {
int res = 0;
for (int i = l + 1; i <= r; i++) {
int k = arr[i];
int j;
for (j = i; j - 1 >= l && arr[j - 1] > k; j--) {
arr[j] = arr[j - 1];
}
arr[j] = k;
res += i - j; // arr[i] 插入到了 arr[j] 的位置
}
return res;
}
}
本文来自博客园,作者:lidongdongdong~,转载请注明原文链接:https://www.cnblogs.com/lidong422339/p/17302891.html