1、 归并排序
归并排序主要由两个步骤组成:划分和合并,每次都划分1/2,需要logn次划分;在合并的过程中,将两个有序的子数组合并成一整个有序的数组所,合并需要遍历所有元素,需要n次操作
时间复杂度为O(nlogn):划分*合并操作
空间复杂度为O(n):用于存储合并时的临时数组长度
1.1 代码实现:
public static void margeSort(int[] arr, int left, int right) {
if (left < right) {
int mid = left + (right - left) / 2;
// 递归的方式对左右数组进行排序
margeSort(arr, left, mid);
margeSort(arr, mid + 1, right);
// 合并左右两个子数组
marge(arr, left,right,mid);
}
}
public static void marge(int[] arr, int left, int right, int mid) {
// 创建临时数组来存放合并后的结果,空间复杂度O(n)
int[] tmp = new int[right - left + 1];
int i = 0;
int p1 = left;
int p2 = mid + 1;
// 将左右子数组的元素按照从小到大的顺序合并到临时数组中
// 这里必须小于等于才能保证归并排序的稳定性
while ( p1 <= mid && p2 <= right) {
if (arr[p1] <= arr[p2]) {
tmp[i++] = arr[p1++];
} else {
tmp[i++] = arr[p2++];
}
}
// 将左子数组中剩余的元素复制到临时数组中
while (p1 <= mid) {
tmp[i++] = arr[p1++];
}
// 将右子数组中剩余的元素复制到临时数组中
while (p2 <= right) {
tmp[i++] = arr[p2++];
}
// 将临时数组中的元素复制回原数组的相应位置
for (int j = 0; j < tmp.length; j++) {
arr[left + j] = tmp[j];
}
}
2、归并排序的变种
2.1 小和问题
问题:求一个数组中每个元素左边比它小的元素的和的总和
// 小和问题
public static int smallSum(int[] arr, int left, int right) {
if (left < right) {
int mid = left + ((right - left) >> 1);
return smallSum(arr, left, mid) +
smallSum(arr, mid + 1, right) +
smallMarge(arr, left, right, mid);
}
return 0;
}
private static int smallMarge(int[] arr, int left, int right, int mid) {
int[] tmp = new int[right - left + 1];
int i = 0;
int p1 = left;
int p2 = mid + 1;
int smallSum = 0;
while (p1 <= mid && p2 <= right) {
// 这里相等的的先放右边的才能保证小和,这样会导致排序不稳定
if (arr[p1] < arr[p2]) {
smallSum += arr[p1] * (right - p2 + 1);
tmp[i++] = arr[p1++];
} else {
tmp[i++] = arr[p2++];
}
}
while (p1 <= mid) {
tmp[i++] = arr[p1++];
}
while (p2 <= right) {
tmp[i++] = arr[p2++];
}
for (int j = 0; j < tmp.length; j++) {
arr[left + j] = tmp[j];
}
return smallSum;
}
2.2 逆序对问题
逆序对问题是指在一个数组中,如果i<j且a[i]>a[j],则称(a[i], a[j])为一个逆序对
// 逆序对问题
// 逆序对问题
public static int reversePairCount(int[] arr, int left, int right) {
if (left < right) {
int mid = left + ((right - left) >> 1);
return reversePairCount(arr, left, mid) +
reversePairCount(arr, mid + 1, right) +
reversePairCount(arr, left, right, mid);
}
return 0;
}
public static int reversePairCount(int[] arr, int left, int right, int mid) {
int[] tmp = new int[right - left + 1];
int margeCount = 0;
int i = 0;
int p1 = left;
int p2 = mid + 1;
while (p1 <= mid && p2 <= right) {
if (arr[p1] < arr[p2]) {
tmp[i++] = arr[p1++];
} else {
// 计算逆序对的数量
margeCount += mid - p1 + 1;
tmp[i++] = arr[p2++];
}
}
while (p1 <= mid) {
tmp[i++] = arr[p1++];
}
while (p2 <= right) {
tmp[i++] = arr[p2++];
}
for (int j = 0; j < tmp.length; j++) {
arr[left + j] = tmp[j];
}
return margeCount;
}