版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/SIMPLE1995/article/details/86514704
一、问题
小和问题
在一个数组中,每一个数左边比当前数小的数累加起来,叫做这个数组的小和。求一个数组 的小和。
例子: [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
二、解析
通过以上例子大致可以看出需要解决那些问题。我们算法中需要找到每一个数左边,比他小的数之和,然后计算每个数的小和之和。解决这个问题最简单粗暴的就是遍历,大致思想就是遍历每一个数,然后遍历每个数左侧比他小的数,并且计算和。最后计算总体小和。这样的算法的时间复杂度也很明显--O(N^2)。我们在前面的博文中介绍了递归思想,归并思想以及相应的算法。小和问题可以采用归并思想。
将数据样本按归并思想分为左右两部分,计算小和,最后归并时计算总体小和。(这里说的或许有些抽象,大家可以看看代码再做相应的理解)
三、代码
package algorithm;
/*
*2019年1月9日
*/
public class SmallSum {
public static int smallSum(int arr[]){
//若数组为空或者只有一个元素 则直接返回0
if (arr==null&&arr.length<2) {
return 0;
}
return mergeSort(arr,0,arr.length-1);
}
public static int mergeSort(int[] arr, int l, int r) {
//递归出口 当左右相等时 即只有一个元素时 返回0
if (l==r) {
return 0;
}
int mid=l+((r-l)>>1);
//递归思想:左侧小和 +右侧小和 + 整体
return mergeSort(arr, l, mid)
+mergeSort(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 i=0;
int res=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(p2<=r){
help[i++]=arr[p2++];
}
while(p1<=mid){
help[i++]=arr[p1++];
}
for (int j = 0; j < help.length; j++) {
arr[l+j]=help[j];
}
return res;//返回当前数组区域的小和
}
public static void main(String[] args) {
//测试
int arr[]={1,1,2,3};
System.out.println(smallSum(arr));
}
}
四、算法时间复杂度
同上篇归并排序算法博文。