小和问题
在一个数组中,每一个数左边的数比当前数小的累加起来,叫做这个数组的小和
举例:
[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
解题思路:分治思想、归并
将一个数组分成等份的子数组,分别为左数组和右数组。由题意可以将题意转换为:统计右边的数比当前数大的数量 * 当前数 = 有多少个数比当前数大
所以可以将左数组和右数组分别进行升序排列,如果右数组的第一个元素大于左数组的第一个元素,说明右数组的所有数都大于左数组。小和就等于 当前值*比当前值大的数量。
/* 在一个数组中,每一个数左边比当前数小的数累加起来,叫做这个数组的小和。求一个数组的小和。 */ public class SmallSum { public static int smallSum(int[] arr) { if (arr == null || arr.length < 2) { return 0; } //在数组长度0-arr.length-1范围上产生小和 return process(arr, 0, arr.length - 1); } 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); } public static int merge(int[] arr, int L, int mid, int R) { int[] help = new int[R - L + 1]; int res = 0; int i = 0; int p1 = L; int p2 = mid + 1; while (p1 <= mid && p2 <= R) { res += arr[p1] < arr[p2] ? (R - p2 + 1) * arr[p1] : 0; //左边严格小于右边,左边和右边相等时先拷贝右边,相等时不能确定右边有几个数比左边大。 help[i++] = arr[p1] < arr[p2] ? arr[p1++] : arr[p2++]; } while (p1 <= mid) { help[i++] = arr[p1++]; } while (p2 <= R) { help[i++] = arr[p2++]; } for (i = 0; i < help.length; i++) { arr[L + i] = help[i]; } return res; } }
通过代码进行理解更容易
L:数组第一个元素
R:数组最后一个元素
P1:指向左数组第一个元素的指针
P2:指向右数组第一个元素的指针
mid:数组中点
help数组:辅助数组,归并排序的合并过程,将排序好的数组拷贝回原数组。
关键点1:如何确定右数组比当前值大的数量:(R - P2 +1))
关键点2:当左数组或右数组越界时,将另一个数组放入辅助数组
关键点3:每一个递归过程都会产生小和
最重要的一点:当左右两个数组有相等元素时,要先将右数组放入辅助数组,因为不确定右数组相等元素的下一个元素是否比左数组的值大,只有完全严格大于左数组的元素时才拷贝。
学习的博客多用于在笔记中,防止笔记过于臃肿,所以将样例及运行结果放在博客中,后以超链接的形式记录在笔记中,所以有些博文过于单薄。如果有小伙伴遇到问题欢迎评论,看到就会回复,学渣一枚,加油努力。