leetcode
leetcode 1351 ~ 1400
子数组和排序后的区间和

子数组和排序后的区间和

难度:

标签:

题目描述

代码结果

运行时间: 62 ms, 内存: 16.3 MB


/*
 * 思路:
 * 1. 使用流来计算所有非空连续子数组的和,并将它们存储在一个列表中。
 * 2. 对列表进行升序排序。
 * 3. 计算指定范围的和,并将其对10^9 + 7取模后返回。
 */

import java.util.*;
import java.util.stream.*;

public class SubarraySumsStream {
    public int rangeSum(int[] nums, int n, int left, int right) {
        List<Integer> subarraySums = IntStream.range(0, n)
            .boxed()
            .flatMap(i -> IntStream.range(i, n)
                .mapToObj(j -> IntStream.rangeClosed(i, j).map(k -> nums[k]).sum())
            )
            .sorted()
            .collect(Collectors.toList());
        int MOD = 1000000007;
        return IntStream.range(left - 1, right)
            .map(subarraySums::get)
            .reduce(0, (a, b) -> (a + b) % MOD);
    }
    public static void main(String[] args) {
        SubarraySumsStream obj = new SubarraySumsStream();
        int[] nums = {1, 2, 3, 4};
        int n = 4;
        int left = 1;
        int right = 5;
        System.out.println(obj.rangeSum(nums, n, left, right)); // 输出:13
    }
}

解释

方法:

这个题解使用了二分查找和前缀和预处理的方法来解决问题。首先,对原数组进行前缀和预处理,得到pre数组和prepre数组,分别表示原数组的前缀和以及前缀和数组的前缀和。然后,通过二分查找来确定第k小的子数组和,同时统计比第k小的数小的子数组和以及等于第k小的数的子数组和,最后将它们相加得到结果。在二分查找的过程中,使用了count函数来计算二维有序数组中小于等于某个数的元素个数。

时间复杂度:

O(n * log(range))

空间复杂度:

O(n)

代码细节讲解

🦆
在题解中使用的二分查找法来确定第k小的子数组和,你是如何确定二分查找的初始`low`和`high`范围的?
在二分查找中,`low`的初始值设为0,因为子数组和不会小于0(考虑到所有元素都非负的情况)。`high`的初始值设为`pre[-1]`,即所有元素的总和,这是因为子数组和的最大可能值就是整个数组的和。
🦆
在`count`函数中,为什么选择`j`的初始值为1而不是0,这样做有什么特别的考虑吗?
在`count`函数中,`j`的初始值为1是因为我们需要计算的是从第i个元素开始的子数组和,即`pre[j] - pre[i]`形式。如果`j`从0开始,那么`pre[i] - pre[0]`将计算从数组开始到第i个元素的和,这不符合我们计算从i到j的子数组和的需求。
🦆
在计算前缀和数组`pre`和`prepre`时,为什么`prepre`数组的维护方式是`prepre.append(prepre[-1] + num)`,这里的`num`应该是`pre[1:]`的元素,它代表的含义是什么?
`pre`数组存储了从数组开始到当前位置的累计和。因此,`pre[1:]`中的每个元素`num`实际上代表从数组开始到当前位置的总和。`prepre`数组是`pre`数组的前缀和,这意味着`prepre`中的每个元素是`pre`数组中所有前面元素的总和。所以,`prepre.append(prepre[-1] + num)`实际上是在累计`pre`数组的值,从而用来快速计算任意两个索引i和j之间的前缀和之差的总和。
🦆
题解中提到的`count`函数的作用是计算小于等于x的子数组的个数,那么这个函数在处理边界时(比如数组的最大或最小元素),是如何确保准确性的?
`count`函数通过滑动窗口的方式来确定满足条件的子数组个数。为了确保边界的准确性,函数内部使用循环和条件判断来精确地控制索引`j`的移动。当`j`不能再向前移动时(即`pre[j] - pre[i]`大于x),循环停止,这保证了不会错过任何可能的子数组。同时,每次循环都会根据不同的i值重新调整j的位置,以确保覆盖所有可能的子数组结构。

相关问题