问题链接
问题描述
给你一个整数数组 nums
和一个整数 k
,请你统计并返回 该数组中和为 k
的子数组的个数 。
子数组是数组中元素的连续非空序列。
示例 1:
输入:nums = [1,1,1], k = 2
输出:2
示例 2:
输入:nums = [1,2,3], k = 3
输出:2
提示:
1 <= nums.length <= 2 * 10^4
-1000 <= nums[i] <= 1000
-10^7 <= k <= 10^7
问题解答
要解决这个问题,我们可以使用前缀和 结合哈希表的方法来高效统计和为 ( k ) 的子数组个数。以下是详细的思路和实现:
思路分析
子数组是数组中连续的非空序列,我们需要统计所有和为 ( k ) 的子数组个数。直接暴力枚举所有子数组的时间复杂度为 ( O(n^2) ),对于较大的数组会超时。因此,我们采用前缀和 + 哈希表的优化方法,将时间复杂度降为 ( O(n) )。
- 前缀和定义 :设
prefixSum[i]
表示数组前i
个元素的和(前缀和)。对于子数组nums[j..i]
(从索引j
到i
,包含两端),其和为prefixSum[i+1] - prefixSum[j]
(假设prefixSum[0] = 0
,prefixSum[1] = nums[0]
,以此类推)。 - 转化问题 :我们需要找到所有满足
prefixSum[i+1] - prefixSum[j] = k
的j
,即prefixSum[j] = prefixSum[i+1] - k
。因此,遍历数组时,只需统计当前前缀和currentSum
对应的currentSum - k
出现的次数即可。 - 哈希表优化 :用哈希表
prefixMap
记录前缀和值 及其出现次数 。遍历数组时,实时计算当前前缀和currentSum
,并查询currentSum - k
在哈希表中的出现次数(即满足条件的j
的数量),累加到结果中。同时,更新当前前缀和的出现次数到哈希表中。
代码实现
java
import java.util.HashMap;
import java.util.Map;
class Solution {
public int subarraySum(int[] nums, int k) {
// 哈希表:键为前缀和值,值为该前缀和出现的次数
Map<Integer, Integer> prefixMap = new HashMap<>();
// 初始条件:前缀和为 0 的情况出现 1 次(对应子数组从第一个元素开始的情况)
prefixMap.put(0, 1);
int currentSum = 0; // 当前前缀和
int result = 0; // 结果计数
for (int num : nums) {
currentSum += num; // 计算当前前缀和
// 查询是否存在前缀和为 currentSum - k 的情况,若存在则累加其出现次数
if (prefixMap.containsKey(currentSum - k)) {
result += prefixMap.get(currentSum - k);
}
// 更新当前前缀和的出现次数(若已存在则加 1,否则设为 1)
prefixMap.put(currentSum, prefixMap.getOrDefault(currentSum, 0) + 1);
}
return result;
}
}
代码解释
- 哈希表初始化 :
prefixMap.put(0, 1)
是关键初始化步骤。假设数组第一个元素的前缀和为nums[0]
,则当nums[0] == k
时,currentSum - k = 0
,此时哈希表中0
的出现次数为 1,确保该情况被正确统计。 - 遍历数组 :逐个元素累加计算当前前缀和
currentSum
。对于每个currentSum
,查询currentSum - k
在哈希表中的出现次数(即有多少个此前的前缀和满足prefixSum[j] = currentSum - k
),并累加到结果。 - 更新哈希表 :将当前前缀和
currentSum
的出现次数更新到哈希表中,供后续元素查询使用。
这种方法的时间复杂度为 ( O(n) )(遍历数组一次,哈希表操作均为 ( O(1) )),空间复杂度为 ( O(n) )(哈希表最多存储 ( n ) 个不同的前缀和),高效解决了问题。