为了避免重复的计算,我们可以对数组 nums 进行预处理,预先存储计算结果。我们使用二维数组 res 存储预处理的结果,res[i][j] 存储 sumRange(i, j) 的返回值。
解法二:空间换时间
java
private int[][] res;// 预处理阶段public NumArray(int[] nums) { int n = nums.length; res = new int[n][n]; for (int i = 0; i < n; i++) { int sum = 0; for (int j = i; j < n; j++) { sum += nums[j]; res[i][j] = sum; } }}public int sumRange(int i, int j) { return res[i][j];}
int n = nums.length;// 计算前缀和数组int[] preSum = new int[n+1];preSum[0] = 0;for (int i = 0; i < n; i++) { preSum[i+1] = preSum[i] + nums[i];}
最终的题解代码如下所示:
java
class NumArray { private int[] preSum; // 预处理阶段 public NumArray(int[] nums) { int n = nums.length; // 计算前缀和数组 preSum = new int[n+1]; preSum[0] = 0; for (int i = 0; i < n; i++) { preSum[i+1] = preSum[i] + nums[i]; } } public int sumRange(int i, int j) { return preSum[j+1] - preSum[i]; }}
这道题关注的是 x 左右两侧的「元素之和」,因此可以考虑用前缀和的技巧来求解。我们发现,x 左侧的元素之和 A 就已经满足前缀和的定义,那么我们以 A 为核心思考解题方法。
x 右侧的元素之和 B 可以直接由 A 求出来。我们设数组的所有元素之和为 S(这个值可以一开始先求出来),则 B 可以表示为 S−A−x。枢纽元素 x 又需要 A=B,那么我们可以得到
A=B=S−A−x
化简得到
2A+x=S
也就是说,前缀和(A)与下一个元素(x)满足以上的关系时,元素 x 即为枢纽元素。我们可以在不断求前缀和的过程中判断以上关系是否满足。
最终得到的题解代码如下:
java
public int pivotIndex(int[] nums) { // 首先计算所有元素之和 S int S = 0; for (int n : nums) { S += n; } int A = 0; // A 为前缀和 // 迭代计算前缀和 for (int i = 0; i < nums.length; i++) { int x = nums[i]; if (2 * A + x == S) { // 满足公式中的关系,x 是枢纽元素 return i; } A += x; // 计算前缀和 } return -1;}
public int subarraySum(int[] nums, int k) { int N = nums.length; // 计算前缀和数组 // presum[k] 表示元素 nums[0..k) 之和 int[] presum = new int[N+1]; int sum = 0; for (int i = 0; i < N; i++) { presum[i] = sum; sum += nums[i]; } presum[N] = sum; // sum of nums[i..j) = sum of nums[0..j) - sum of nums[0..i) int count = 0; for (int i = 0; i <= N; i++) { for (int j = i+1; j <= N; j++) { // 前缀和相减求子数组之和 if (presum[j] - presum[i] == k) { count++; } } } return count;}