在一个数组中, 每一个数左边比当前数小的数累加起来, 叫做这个数组的小和。
例子:
[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
其实小和问题就是归并排序的应用。不了解归并排序的,可以看一下这篇文章归并排序
废话不多说,直接上代码
package com.chenli.sort;
/**
* 归并排序的应用:小和问题
* @author chenli
*
*/
public class SmallSum {
public static int smallSum(int[]arr){
if(arr==null||arr.length<2){
return 0;
}
return mergeSort(arr,0,arr.length-1);
}
private static int mergeSort(int[] arr, int left, int right) {
//递归结束条件
if(left == right){
return 0;
}
//相当于mid =(left+right)/2 即 mid=left+(right-left)/2
//使用这种形式是为了防止溢出。为什么使用位运算?因为位运算在电脑里比算术运算快
int mid = left + ((right-left)>>1);
return mergeSort(arr, left, mid)
+mergeSort(arr, mid+1, right)
+merge(arr,left,mid,right);
}
private static int merge(int[] arr, int left, int mid, int right) {
//定义一个辅助数组
int[]help = new int[right-left+1];
//定义一个指向左边有序数组的第一个数的下标的指针
int p1 = left;
//定义一个指向右边有序数组的第一个数的下标的指针
int p2 = mid +1;
//记录小和数的结果
int result = 0;
//辅助数组开始的下标
int i = 0;
while(p1<=mid&&p2<=right){
/*
if(arr[p1]<arr[p2]){
result = result + (p2-mid+1)*arr[p1];
}
如果arr[p2]>arr[p1],说明arr[p2]后面的值都大于arr[p1]。
所以直接记录有几个大于arr[p1]的值
*/
result += arr[p1]<arr[p2]?(right-p2+1)*arr[p1]:0;
help[i++] = arr[p1]<arr[p2]?arr[p1++]:arr[p2++];
}
while(p1<=mid){
help[i++]=arr[p1++];
}
while(p2<=right){
help[i++]=arr[p2++];
}
for(int k=0;k<help.length;k++){
arr[left+k]=help[k];
}
return result;
}
public static void main(String[] args) {
int[]arr = {1,3,4,2,5};
int smallsum = smallSum(arr);
System.out.println("小和的值为:"+smallsum);
}
}
运行结果