归并排序之小和问题

问题描述:

在一个整型数组中每一个数左边比当前数小的数累加起来称为该数组的小和。
如 {1,3,5,4,2};
1的左边比1小的数: 没有
3的左边比3小的数: 1
5的左边比5小的数: 1 ,3
4的左边比4小的数: 1,3
2的左边比2小的数: 1
因此该数组的小和为: 1+1+3+3+1+1 = 10.

暴力解法 ----- 遍历求解

依次遍历,满足要求时求和
Java代码如下:

package www.test.java;
public class Test {
    public static void main(String[] args) {
        int[] arr = new int[]{1,3,5,4,2};
        int smallsum = getSmallSum1(arr);
        System.out.println(smallsum);
    }

    /**
     * 遍历每个数的左边,满足条件时求总和
     * @param arr
     * @return
     */
    public static int getSmallSum1(int[] arr){
        int sum = 0;
        for(int i = 0; i < arr.length; i++){
            for(int j = i - 1;j >=0; j--){  
                if(arr[i] > arr[j]){
                    sum += arr[j];
                }
            }
        }
        return sum;
    }
}

输出结果:
归并排序之小和问题
当然对该题目我们可以换一下思路,题目要求我们求左边小于该数的数的和,我们也可以看右边有多少个数比该数大,然后将这个数加几遍。大概思路如下:
如 {1,3,5,4,2};
1的右边比1大的数有4个 : 4 * 1
3的右边比3大的数有2个 : 2 * 3
5的右边比5大的数有0个 : 0 * 5
4的右边比4大的数有0个 : 0 * 4
2的右边比2大的数由0个 : 0 * 2
总和 4 + 6 = 10。

Java代码如下:

package www.test.java;
public class Test {
    public static void main(String[] args) {
        int[] arr = new int[]{1,3,5,4,2};
        int smallsum = getSmallSum2(arr);
        System.out.println(smallsum);
    }
        public static int getSmallSum2(int[] arr){
        int sum = 0;
        for(int i = 0; i < arr.length;i++){
            for(int j = i + 1; j < arr.length; j++){
                if(arr[j] > arr[i]){
                    sum += arr[i];
                }
            }
        }
        return sum;
    }

}

输出结果:
归并排序之小和问题
虽然这两种方式都可以解决这个问题,但时间复杂度颇高均为O(N * N).

利用归并排序

归并排序将无序的数组分为一直等分,直到分到每个小数组中的元素都为1个时候每一个自然就变成了有序数组。只要将每一个数组按照顺序合并起来就整体变成了一个有序数组。那么如何合并呢?如图所示:
归并排序之小和问题
这个为什么快呢?因为在求小和的过程中后边子数组已经是有序的了,只要求出它的长度,在乘以当前数组的长度就可以求出小和的大小了,而不需要再遍历数组。
代码如下:`


public class Test {
    public static void main(String[] args) {
        int[] arr = new int[]{1,3,5,4,2};
        System.out.println(mergeSort(arr));
    }

    public static int mergeSort(int[] arr) {
        if (arr == null || arr.length < 2) {
            return 0;
        }
        return sortProcess(arr, 0, arr.length - 1);
    }

    public static int sortProcess(int[] arr, int L, int R) {
        if (L == R) {
            return 0;
        }
        int mid = L + ((R - L) >> 1);
        return sortProcess(arr, L, mid) 
                + sortProcess(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 sum = 0;
        int i = 0;
        int p1 = L;
        int p2 = mid + 1;
        while (p1 <= mid && p2 <= R) { //该循环结束必有一个越界,不存在同时越界
            sum += 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 (i = 0; i < help.length; i++) {
            arr[L + i] = help[i];
        }
        return sum;
    }
}

运行结果:
归并排序之小和问题