小和问题和逆序对
题目描述:
小和问题:在随机元素,随机数组大小的数组中,找出左边比右边元素小的所有元素之和。
示例1:
数组[4,2,5,1,7,3,6] 第一个元素4比2大,不算小和,5比4和2都大,那就是4+2=6;1比4和2和5都小,不算小和;7比前面的都大,那就是上次小和6+4+2+5+1=18;然后3前面比2和1大,那就是18+2+1=21;最后6比4、2、5、1、3都大,结果就是21+4+2+5+1+3=36。那么最后的结果就是36。
题目思路:
求小和问题,本题采用递归算法的思想。将数组分为左右两个部分求解,若左侧的数比右侧的数小,则一定比右侧数后面所有的数都要小。则此时的最小和为res=(r - right + 1) * arr[left],然后采用递归的思想依次求和累计。
其实本题采用递归的思想,要求右侧比左侧大的数。主要是看中了递归排序的稳定性。因为稳定性,才不会打乱原数组的顺序。
题目难度:
代码实现:
public static int mergeSortSum(int[] arr) {
if (arr == null || arr.length < 2) {
return 0;
}
return mergeSortSum(arr, 0, arr.length - 1);
}
public static int mergeSortSum(int[] arr, int begin, int end) {
if (begin == end) {
return 0;
}
int mid = begin + ((end - begin) >> 1);
return mergeSortSum(arr, begin, mid) + mergeSortSum(arr, mid + 1, end) + merge(arr, begin, mid, end);
}
public static int merge(int[] arr, int l, int mid, int r) {
int left = l;
int right = mid + 1;
int[] help = new int[r - l + 1];
int i = 0;
int res = 0;
while (left <= mid && right <= r) {
//一旦在右边遇到比当前值大的,则之后的数一定比当前值大
res += arr[left] < arr[right] ? (r - right + 1) * arr[left] : 0;
help[i++] = arr[left] < arr[right] ? arr[left++] : arr[right++];
}
while (left <= mid) {
help[i++] = arr[left++];
}
while (right < r) {
help[i++] = arr[right++];
}
for (int j = 0; j < help.length; j++) {
arr[l + j] = help[j];
}
return res;
}