在一个数组中,每一个数左边比当前数小的数累加起来,叫做这个数组的小和。求一个给定数组的小和。
例子:数组为:[1,3,4,2,5]
1左边比1小的数:没有
3左边比3小的数:1
4左边比4小的数:1,3
2左边比2小的数:1
5左边比5小的数:1,3,4,2
所以小和为1+(1+3)+1+(1+3+4+2)=16
思路:找每一个数右边比当前数大的个数,(个数 * 当前数) 的累加和就是结果。
这咋和归并排序联系上的呢,仔细想想,在左组和右组merge的时候,会比较数的大小,这时就可以在右组找到比左组当前数大的个数。
public class SmallSum {
public static void main(String[] args) {
int[] arr = {1, 3, 4, 2, 5};
int smallSumValue = mergeSortAndCalculate(arr, 0, arr.length - 1);
System.out.println("小和为:" + smallSumValue);
}
private static int mergeSortAndCalculate(int[] arr, int left, int right) {
if (left == right) {
return 0;
}
int mid = left + ((right - left) >> 1);
return mergeSortAndCalculate(arr, left, mid) +
mergeSortAndCalculate(arr, mid + 1, right) +
mergeAndCalculate(arr, left, mid, right);
}
private static int mergeAndCalculate(int[] arr, int left, int mid, int right) {
int[] temp = new int[right - left + 1];
int p1 = left;
int p2 = mid + 1;
int i = 0;
int res = 0;
while (p1 <= mid && p2 <= right) {
if (arr[p1] < arr[p2]) {
//因为右边是排序好的,arr[p2]大,后边的肯定大,只需要求个数就行了
// 数*右边的个数
res += arr[p1] * (right - p2 + 1);
temp[i++] = arr[p1++];
} else {
temp[i++] = arr[p2++];
}
}
while (p1 <= mid) {
temp[i++] = arr[p1++];
}
while (p2 <= right) {
temp[i++] = arr[p2++];
}
for (i = 0; i < temp.length; i++) {
arr[left + i] = temp[i];
}
return res;
}
}
设有一个数组 [a1, a2, a3,… an],对于数组中任意两个元素ai,aj,若i
例子:3 5 2 1 0 4 9
所有逆序对是:(3,2),(3,1),(3,0),(5,2),(5,1),(5,0),(5,4),(2,1),(2,0),(1,0)。逆序对个数为10。
思路:
合并的时候,从右往左合并,(此时右组位置 - mid位置) 的累加和 即是逆序对个数。
这又咋和归并排序联系上的呢,仔细想想,在左组和右组merge的时候,会比较数的大小,但是我要找到的是右边更小的,所以可以采用从右往左合并的方式;同时在处理相等的时候,需要先拷贝右组的,这样才能准确找出右组小的个数。
public class InversionCount {
public static void main(String[] args) {
int[] array = {2, 4, 3, 1, 5};
int inversionCount = mergeSortAndCount(array, 0, array.length - 1);
System.out.println("逆序对的数量为:" + inversionCount);
}
private static int mergeSortAndCount(int[] arr, int left, int right) {
if (left >= right) {
return 0;
}
int mid = left + (right - left) / 2;
int leftInversions = mergeSortAndCount(arr, left, mid);
int rightInversions = mergeSortAndCount(arr, mid + 1, right);
int mergeInversions = mergeAndCount(arr, left, mid, right);
return leftInversions + rightInversions + mergeInversions;
}
private static int mergeAndCount(int[] arr, int left, int mid, int right) {
int[] temp = new int[right - left + 1];
int i = left;
int j = mid + 1;
int k = 0;
int inversions = 0;
while (i <= mid && j <= right) {
if (arr[i] <= arr[j]) {
temp[k++] = arr[i++];
} else {
temp[k++] = arr[j++];
inversions += mid - i + 1;
}
}
while (i <= mid) {
temp[k++] = arr[i++];
}
while (j <= right) {
temp[k++] = arr[j++];
}
for (int l = 0; l < temp.length; l++) {
arr[left + l] = temp[l];
}
return inversions;
}
}