赞
踩
在一个数组中,每一个数左边比当前数小的数累加起来,叫做这个数组的小和。求一个数组的小和。
例如:[1,3,4,2,5] 1左边比1小的数没有;3左边比3小的数1,4左边有1,3;2左边有1;5左边有1,3,4,2。所以这个数组的小和为1+1+3+1+1+3+4+2=16。
方法1:暴力算法,直接遍历就行了。但是时间复杂度为O(N^2)。
方法2:可以使用并归排序的思路。转换一下,等价于右边比当前数大的有几个,比如说当前数是1,整个数组中右边比1大的有4个,那么就先有4个1相加,然后把当前数向后移,也就是变为了3,3右边比3大的有两个,所以之前4个1再加上2个3,后面再类似的算出有数量然后相加即可。
例子分析:[1,3,4,2,5,]。先取中点索引为2位置分成两个区域。[1,3,4] [2,5]。用p1指针指向第一个数组中的1,p2指针指向第二个数组中的2。p1指针指向的值比p2小,此时计算第二个数组中包括p2在内的有多少个元素,此时有两个,那么就是2个1,此时把p1指向的值拷贝到一个新数组中并且将p1指针向下移动指向3,此时p1指向的值比p2指向的值要大,拷贝第二个数组p2指向的值到之前的那个新数组中,p2的指针向下移动指向5,又比较p1和p2指向的值的大小,发现5比3要大,计算包括5在内的后面的元素数量,为1。那么此时就是1个3。此时又把p1指针的值3拷贝到新数组中(此时新数组为[1,2,3]每次进行拷贝都能保证这个新数组是有序的)然后把p1指向下一个指向4,又比较p1和p2的值大小,发现4比5要小,计算5那边的数量为1.此时就是1个4。然后把p1的4拷贝到新数组中,p1指针下移发现越界,此时直接把第二个数组中的剩下元素拷贝进新数组中([1,2,3,4,5])。这个合并的过程可以得到左边的数组中在右边的数组中的最小和,进行递归操作,[1,3,4]分为[1,3] [4],[1,3]最终分为[1] [3]。[2,5]分为[2] [5]。[1]和[3]进行合并的时候可以计算得到[1,3]有序数组和1个1最小和,然后用[1,3]去和[4]进行合并,得到有序数组[1,3,4]并且得到1个1和1个3。而[2,5]这边的话,[2]和[5]合并得到新数组[2,5]和1个2。最后就是[1,3,4]和[2,5]进行合并得到最终的所有小和,把小和加起来就可以了,首先是[1][3]的1个1。然后是[1,3] [4]的1个1和1个3,然后是[2][5]的1个2,然后是[1,3,4] [2,5]中的2个1,1个3,1个4。
1+1+3+2+1+1+3+4=16。
Java代码如下
- public static void main(String[] args) {
- int[] arr = new int[]{1, 3, 4, 2, 5};
- System.out.println(getSmall(arr,0,arr.length-1));
- }
- //求数组的小和问题。
- public static int getSmall(int[] arr,int l,int r){
- if (l == r ){
- return 0;
- }
- int mid = l + ((r-l)>>1);
- return getSmall(arr,l,mid)+getSmall(arr,mid+1,r)+merge(arr,l,mid,r);
- }
-
- private static int merge(int[] arr, int l, int mid, int r) {
- int[] help = new int[r-l+1];
- int i=0;
- int p1 = l;
- int p2 = mid+1;
- int res=0;
- while (p1<=mid && p2<=r){
- //其实就是在并归的时候利用排好序的这个性质直接算出右侧大于左侧的数量然后落实到每一个元素,中间节省了很多比较。
- res += arr[p1] < arr[p2] ? arr[p1] * (r-p2+1):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 j = 0 ; j<help.length;j++){
- arr[l+j] = help[j];
- }
- return res;
- }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。