赞
踩
一、问题
小和问题
在一个数组中,每一个数左边比当前数小的数累加起来,叫做这个数组的小和。求一个数组 的小和。
例子: [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));
-
- }
-
- }
四、算法时间复杂度
同上篇归并排序算法博文。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。