问题描述
在一个数组中, 每一个数左边比当前数小的数累加起来, 叫做这个数组的小和。 求一个数组的小和。
样例
[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
题解
- 对数组进行归并排序,在合并的过程中,如果左侧部分的当前元素l小于右侧部分的当前元素r,那么计算右侧部分当前位置到结束位置的元素个数,即为两部分合并之后的数组中在i右侧的元素个数;否则右侧指针下移。
- 不会出现重复计算的情况,因为每次都是各部分之间比较,内部并不进行比较。
public static int smallSum(int[] arr) {
if(arr==null||arr.length<2)
return 0;
return smallSum(arr,0,arr.length-1);
}
private static int smallSum(int[] arr, int i, int j) {
if(i==j)
return 0;
int mid=i+(j-i)/2;
return smallSum(arr,i,mid)+smallSum(arr,mid+1,j)+mergeSum(arr,i,j,mid);
}
private static int mergeSum(int[] arr, int i, int j, int mid) {
int s=0;
int ii=i,ij=mid+1;
int []help=new int[j-i+1];
int k=0;
while(ii<=mid&&ij<=j) {
if(arr[ii]<arr[ij]) {
s+=(j-ij+1)*arr[ii];
help[k++]=arr[ii++];
}else {
help[k++]=arr[ij++];
}
}
while(ii<=mid)
help[k++]=arr[ii++];
while(ij<=j)
help[k++]=arr[ij++];
for(k=0;k<help.length;k++) {
arr[i+k]=help[k];
}
return s;
}
应用2:在一个数组中, 左边的数如果比右边的数大, 则折两个数构成一个逆序对, 请打印所有逆序对