开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第27天,点击查看活动详情
前言
本文介绍前缀和数组的基本概念以及两种代码实现。
概念
当要求频繁的对一个数组计算在一定的索引区间的元素之和时,我们可以提前把数据计算好,放到一个数组里,这个数组就被称为前缀和数组。
比如,给定一个数组arr,计算从下标L到下标R之间元素的和,而且要求计算不同下标之间的元素和,极其频繁的调用。
当然简单的方法可以每次都去进行遍历计算,但是如果说调用量次数特别多,上千万次、上亿次,这样每次都去循环遍历判断,效率就差的很多了。
要想提高效率的话,我们可以提前把数组计算出来,放到一个新的数组里,这样每次计算的时候直接去新数组里取就好了,也就是空间换时间的方法,这新的数组就是我们所说的前缀和数组,一般的分为一维前缀和数组、二维前缀和数组。
一维前缀和数组
一维前缀和数组需要准备一个新的数组,把下标0到下标N的数据分别计算出来放入新的数组中,这个新数组help的含义为:
help[i]的值代表arr[0] + arr[1] + ... + arr[i]的元素之和。
如果我们要计算3到6之间元素的累加和的话,我们来看一下,help[6]代表arr[0]到arr[6]之间元素的累加和,help[2]代表arr[0]到arr[2]之间元素的累加和。那么3到6之间元素的累加和就为help[6]-help[2]。
如果要计算0到6之间的累加和的话,直接取help[6]即可。
代码实现
public class Code07_RangeNum {
private int[] prefixSum;
public Code07_RangeNum(int[] arr) {
prefixSum = new int[arr.length];
prefixSum[0] = arr[0];
for (int i = 1; i < arr.length; i++) {
prefixSum[i] = prefixSum[i - 1] + arr[i];
}
}
public int sumRange(int L, int R) {
return L == 0 ? prefixSum[R] : prefixSum[R] - prefixSum[L - 1];
}
public static void main(String[] args) {
int[] arr = {3, 5, 2, 7, 1, 8, 13, 9, 12};
Code07_RangeNum rangeNum = new Code07_RangeNum(arr);
int sum = rangeNum.sumRange(1, 6);
System.out.println(sum);
}
}
二维数组实现思路
我们可以创建一个正方形张表,横边为R、竖边为L,如图。
因为L<=R,所以对角线下面的数据是不可能存在的,也就是空的。当我们要计算L到R上的元素之和时,只需要找到二维数组中对应的数据即可。
在生成这个表示,会生成L * R个元素,因为有一部分是不可能存在的,所以最后需要(L * R) / 2个元素。
代码实现
public class Code08_RangeNum {
private int[][] prefixSum;
public Code08_RangeNum(int[] arr) {
prefixSum = new int[arr.length][arr.length];
for (int i = 0; i < arr.length; i++) {
prefixSum[i][i] = arr[i];
for (int j = i + 1; j < arr.length; j++) {
prefixSum[i][j] = prefixSum[i][j - 1] + arr[j];
}
}
}
public int sumRange(int L, int R) {
return prefixSum[L][R];
}
public static void main(String[] args) {
int[] arr = {3, 5, 2, 7, 1, 8, 13, 9, 12};
Code08_RangeNum rangeNum = new Code08_RangeNum(arr);
int sum = rangeNum.sumRange(1, 6);
System.out.println(sum);
}
}