数组小和定义:
在一个数组中,一个数左边比它小的数的总和,叫数的小和,所有数的小和累加,叫数组小和。
例子:数组 [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 int smallSum(int[] arr) {
if (arr == null || arr.length < 2) {
return 0;
}
return process(arr, 0, arr.length -1);
}
//arr[L...R]既要排好序,也要求小和返回
private static int process(int[] arr, int l, int r) {
if (l == r) {
return 0;
}
int mid = l + ((r - l) >> 1);
return process(arr, l, mid)//左组的小和
+
process(arr,mid + 1, r)//右组的小和
+
merge(arr, l, mid, r);//merge的小和
}
//把L...mid和mid+1...R的数merge到一起,使之整体有序
private static int merge(int[] arr, int L, int M, int R) {
//辅助数组
int[] help = new int[R - L + 1];
//辅助数组下标
int i = 0;
//p1为左部分数组下标开始位置
int p1 = L;
//p2为右部分数组下标开始位置
int p2 = M + 1;
int res = 0;//L...R的小和
//p1和p2都还没越界,谁小谁拷贝到help数组
while (p1 <= M && p2 <= R) {
res += arr[p1] < arr[p2] ? (R - p2 + 1) * arr[p1] : 0;
//谁小谁拷贝到help数组,相等先拷贝右边
help[i++] = arr[p1] < arr[p2] ? arr[p1++] : arr[p2++];
}
//下面两个while自会有一个执行,因为p1和p2不可能同时越界
while (p1 <= M) {
//把p1剩下的元素全部拷贝到help
help[i++] = arr[p1++];
}
while (p2 <= R) {
//把p2剩下的元素全部拷贝到help
help[i++] = arr[p2++];
}
//把help数组元素刷回arr
for (i = 0; i < help.length; i++) {
arr[L + i] = help[i];
}
return res;
}
public static void main(String[] args) {
int[] arr = new int[]{1,3,4,2,5};
System.out.println(smallSum(arr));
}
}