小和归并

给一个数组,对每个元素计算数组左边比当前元素小的元素和,最后将这些结果再求和。例如{1, 3, 5, 2, 4, 6}的小和为1+4+1+6+15=27

这个题实际上是逆序数的变形,以元素3来说,后面的5,4,6比3大,所以小和都会将其计算一次,所以我们想要计算是对于任意元素i,右边比i大的元素个数。考虑归并排序能将左右两个有序数组合并,对于左边的任意元素i,都能计算得到右边比i大的元素个数。每一轮合并都能计算的到元素i右边比其大的元素个数,经过所有合并之后,就得能得到原数组中在i右边且比i大的元素个数。

  1. 为什么不考虑左边比i大的元素?防止重复计算,因为最初是从长度为1的数组开始合并的,所以左边比i大的元素都已经计算过一次了。
public class SmallSumMerge {
    public static void main(String[] args) {
        int[] arr = {1, 3, 5, 2, 4, 6};
        System.out.println(smallSum(arr));
    }

    private static int smallSum(int[] arr) {
        if (arr == null || arr.length <= 1) {
            return 0;
        }
        int[] temp = new int[arr.length];
        return mergeSort(arr, 0, arr.length, temp);
    }

    private static int mergeSort(int[] source, int fromIndex, int toIndex, int[] temp) {
        if (fromIndex + 1 >= toIndex) {
            return 0;
        }
        int mid = (fromIndex + toIndex) >> 1;
        int sum = mergeSort(source, fromIndex, mid, temp);
        sum += mergeSort(source, mid, toIndex, temp);
        return sum + merge(source, fromIndex, toIndex, temp);
    }

    private static int merge(int[] source, int fromIndex, int toIndex, int[] temp) {
        int mid = (fromIndex + toIndex) >> 1;
        int leftIndex = fromIndex, rightIndex = mid;
        int tempIndex = fromIndex;
        int sum = 0;
        while (leftIndex < mid && rightIndex < toIndex) {
            if (source[leftIndex] <= source[rightIndex]) {
                temp[tempIndex++] = source[leftIndex];
                sum += source[leftIndex] * (toIndex - rightIndex);
                leftIndex++;
            } else {
                temp[tempIndex++] = source[rightIndex++];
            }
        }
        int restIndex = leftIndex < mid ? leftIndex : rightIndex;
        while (tempIndex < toIndex) {
            temp[tempIndex++] = source[restIndex++];
        }
        // copy back to source
        for (int i = fromIndex; i < toIndex; i++) {
            source[i] = temp[i];
        }
        return sum;
    }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值