小和问题
描述
在一个数组中,每一个数左边比当前数小的数累加起来,叫做这个数组的小和。求一个数组的小和。
例子
[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
首先想到的方法是遍历,两次for循环遍历数组,将每一个和他前面的数字比过去,只要比这个数小就与结果相加,最终可以在O(n^2)的时间复杂度上实现这道题。
int Comparator (int *a ,int n) { if (a == NULL || n < 2) return 0; int sum=0; for(int i=0;i<n;i++) { for(int j=0;j<i;j++) { if (a[i] > a[j]) sum +=a[j]; } } return sum; }
但是n^2的复杂度过于高了,有没有方法可以简化呢?
有!
首先这个问题可以等同于归并排序,利用递归的思想,将数组不断查分成有序的一个个小序列,然后在每次merge归并的时候求值。
首先每次归并函数都是将两个已经排完序的数组进行外排,有两个指针p1,p2分别指向两个数组,把小的数字先放在一个help数组里然后把指针往后移,这时候我们发现每一个左数组的值进行放入temp时,它的值一定会比右指针后面的数字小,所以每一个数字一定会小于右数组(r-p2)个数,即小和的总和就需要加上(r-p2)*左指针p1指向的值。
int merge(int* &arr ,int l, int mid ,int r); int Mergesort (int* &arr ,int l ,int r); int SmallSum (int* &arr ,int n) { if (arr ==NULL || n<2 ) return 0; return Mergesort(arr,0,n-1); } int Mergesort (int* &arr ,int l ,int r) { 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); } // 1 3 9 || 2 4 7 int merge(int* &arr ,int l, int mid ,int r) { int p1=l; int p2=mid+1; int i=0; int res=0; int* help = (int *)malloc (sizeof (int) * (r - l + 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(p1 <= mid) { help[i++] = arr[p1++]; } while(p2 <= r) { help[i++] =arr[p2++]; } for(int i=l;i<=r;i++) { arr[i]=help[i-l]; } return res; }