子数组和排序后的区间和
难度:
标签:
题目描述
代码结果
运行时间: 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`范围的?
▷🦆
在`count`函数中,为什么选择`j`的初始值为1而不是0,这样做有什么特别的考虑吗?
▷🦆
在计算前缀和数组`pre`和`prepre`时,为什么`prepre`数组的维护方式是`prepre.append(prepre[-1] + num)`,这里的`num`应该是`pre[1:]`的元素,它代表的含义是什么?
▷🦆
题解中提到的`count`函数的作用是计算小于等于x的子数组的个数,那么这个函数在处理边界时(比如数组的最大或最小元素),是如何确保准确性的?
▷