【知识积累】在一个数组中,一个数左边比它小的数的总和,叫数的小和,所有数的小和累加起来,叫数组小和,求数组小和

package com.example.demo.algorithm.D003;

/**
 * @Description :
 * 小和问题:理解归并排序的精髓
 *
 * 在一个数组中,一个数左边比它小的数的总和,叫数的小和,所有数的小和累加起来,叫数组小和,求数组小和。
 * 例子:[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
 *
 * 流程:
 * 右组的某个数比左组的大的有几个 == 左组的某个数在右组中有几个比它大,即右组的数就
 * 1、左组去marge,返回小和
 * 2、右组去marge,返回小和
 * 3、左组和右组marge,返回小和
 * [1,3,4,2,5]
 *  0,1,2,3,4
 * 134为左组  25为右组
 * p1在0位置  p2在3位置
 * 1 < 2 产生小和  从4的下标开始算有2个数大于1  所以小和为2个1 拷贝p1 p1++
 * help[1]
 *
 * p1在1位置 p2在3位置
 * 3 >(=) 2 不产生小和 拷贝p2 p2++
 * help[1,2]
 *
 * p1在1位置 p2在4位置
 * 3 < 5 产生小和 从5的下标开始算有1个数大于3 所以小和为1个3 拷贝p1 p1++
 * help[1,2,3]
 *
 * p1在2位置 p2在4位置
 * 4 < 5 产生小和  从5的下标开始算有1个数大于4 所以小和为1个4 拷贝p1 p1++
 * help[1,2,3,4]
 *
 * 注:
 * 左组数小于右组的数,产生小和为右组下标开始数有n个数比左组大,即n*左组数为小和,拷贝左组的数到help数组,左组下标++
 * 左组数大于等于右组的数,不产生小和,拷贝右组的数到help数组,右组下标++
 *
 * 4、整体返回小和
 *
 * @Author : Darren
 * @Date : 2021 年 02 月 21 日 18:35:44
 * @since : 1.0
 */
public class J002_SmallSum {

    public static void main(String[] args) {
        int testTime = 500000;
        int maxSize = 100;
        int maxValue = 100;
        boolean succeed = true;
        for (int i = 0; i < testTime; i++) {
            int[] arr1 = generateRandomArray(maxSize, maxValue);
            int[] arr2 = copyArray(arr1);
            if (smallSum(arr1) != smallSum2(arr2)) {
                succeed = false;
                printArray(arr1);
                printArray(arr2);
                break;
            }
        }
        System.out.println(succeed ? "Nice!" : "Fucking fucked!");
    }


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

    public static int smallSum2(int[] arr){
        if (arr == null || arr.length < 2){
            return 0;
        }
        int res = 0;
        for (int i = 1; i < arr.length; i++) {
            for (int j = 0; j < i; j++) {
                res += arr[j] < arr[i] ? arr[j] : 0;
            }
        }
        return res;
    }


    /**
     * arr[l..r]既要排好序,也要求小和返回
     * 所有merge时,产生的小和累加
     * 左排序 merge
     * 右排序 merge
     * @param arr
     * @param l
     * @param r
     * @return
     */
    private static int process(int[] arr, int l, int r) {
        //只有一个数,不产生小和
        if (l == r){
            return 0;
        }
        int mid = l + ((r - l) >> 1);
        //l …… mid的小和  + mid+1……r的小和 + l……r的小和
        return process(arr, l, mid)
        + process(arr, mid + 1, r)
        + marge(arr, l, mid, r);
    }

    private static int marge(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){
            // 左组数小于右组的数,产生小和为右组下标开始数有n个数比左组大(因为有序),即n*左组数为小和,拷贝左组的数到help数组,左组下标++
            // 左组数大于等于右组的数,不产生小和,拷贝右组的数到help数组,右组下标++
            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 i1 = 0; i1 < help.length; i1++) {
            arr[l + i1] = help[i1];
        }
        return res;
    }

    /**
     * 生成一个随机数组
     * @param maxSize
     * @param maxValue
     * @return
     */
    public static int[] generateRandomArray(int maxSize, int maxValue){
        //Math.random() [0,1)
        //Math.random() * N [0,N)
        //(int)(Math.random() * N) [0,N-1]
        int[] arrays = new int[(int) ((maxSize+1) * Math.random())];
        for (int i = 0; i < arrays.length; i++) {
            arrays[i] = (int) ((maxValue+1) * Math.random()) - (int)(maxValue * Math.random());
        }
        return arrays;
    }

    /**
     * 打印数组
     * @param arrays
     */
    public static void printArray(int[] arrays){
        if (arrays == null){
            return;
        }
        for (int i = 0; i < arrays.length; i++) {
            System.out.print(arrays[i] + " ");
        }
        System.out.println();
    }

    /**
     * 复制数组
     */
    public static int[] copyArray(int[] arr){
        if (arr == null){
            return null;
        }
        int[] res = new int[arr.length];
        for (int i = 0; i < arr.length; i++) {
            res[i] = arr[i];
        }
        return res;
    }

    /**
     * 判断两个数组是否相等
     * @param arr1
     * @param arr2
     * @return
     */
    public static boolean isArrayEqual(int[] arr1, int[] arr2){
        if ((arr1 == null && arr2 != null) || (arr1 != null && arr2 == null)){
            return false;
        }

        if (arr1 == null && arr2 == null){
            return true;
        }

        if (arr1.length != arr2.length){
            return false;
        }

        for (int i = 0; i < arr1.length; i++) {
            if (arr1[i] != arr2[i]){
                return false;
            }
        }
        return true;
    }

}

おすすめ

転載: blog.csdn.net/axin1240101543/article/details/114089879