问题:查询给定数组 [1, 2, 3, 4] 的 n~m 范围内的和?
直接算的话我们可以遍历 n ~ m 求和,如果查询次数很频繁的话,就会消耗大量的时间,因此我们可以设计一个数据结构进行预处理。
方法一
采用 4 * 4 矩阵存储,直接将 n 到 m 的值全部计算出置入二维数组,需要用到直接取。
0 | 1 | 2 | 3 | |
---|---|---|---|---|
0 | 1 | 3 | 6 | 10 |
1 | 2 | 5 | 9 | |
2 | 3 | 7 | ||
3 | 4 |
此法会存在 n * n / 2 大小的空间浪费,并且建立该表示时间消耗大。
方法二
采用前缀和方式存储,采用一维数组的方式存储,实际上就是上述矩阵的第一行,该一维数组中存储的是从 0 到 n 的累加值,当咱们需要取从 n 到 m 的累加值时,直接使用 arr[n] - arr[m - 1] 即可。
前缀和数组
0 | 1 | 2 | 3 |
---|---|---|---|
1 | 3 | 6 | 10 |
代码:
public class PreSum {
static int[] preSum;
public PreSum(int[] arr) {
preSum = new int[arr.length];
preSum[0] = arr[0];
for (int i = 1; i < arr.length; i++) {
preSum[i] = preSum[i - 1] + arr[i];
}
}
public static int rangeSum(int n, int m) {
return m == 0 ? preSum[n] : preSum[n] - preSum[m - 1];
}
}
优劣
第二种方式非常简洁,但是在查询次数远超出数据空间时,此时矩阵存储会更好,直接取出值,而不需要计算一下再将值取出。
比如,在 1000 长度的数组中计算 n 到 m 的累加,进行上亿次查询,这时整个数据空间才 100 万,直接取值显然比计算后再取值快很多,空间消耗也可以接受。