一、题目描述
给你一个整数数组 nums 和一个整数 k ,请你统计并返回 该数组中和为 k 的连续子数组的个数 。
示例:
二、思路
我记得之前我在一篇文章中说,看见连续子数组,就要立刻想到滑动窗口,现在我收回这句话。
这道题是一道很典型的前缀和问题 —— 那么什么是前缀和问题呢?
前缀和问题就是用 sum[i] - sum[j]
快速计算数组 [j ,i] 元素和的问题。详细来说,就是通过预处理计算出以第 0 个元素起始,到第 i 个元素结尾的元素之和,并将其记录为数组 sum,从而实现通过 sum[i] - sum[j]
快速计算 [j ,i] 位置的元素和,这样的问题就是前缀和问题。见下图示例:
三、代码
、
对本题而言,其重点在于实现一个转换,即将 ([j , i]元素的和 == k
) 转化为 (sum[i] - sum[j] == k
) 进而转化为 (sum[i] - k == sum[j]
),从而这道题就变成了找在下标 i 之前的指定的 sum[j] 的数量是多少的问题了。
从而,我们使用一 map 在遍历过程中存储 sum[j] 出现的次数,具体实现代码如下,详情请见注释
// 和为 k 的子数组数目 —— 前缀和问题
public int subarraySum(int[] nums, int k) {
int res = 0;
if(nums == null || nums.length==0){ // 空数组处理
return res;
}
int len = nums.length;
if(len == 0){
return res;
}
int[] sum = new int[len];
sum[0] = nums[0];
for(int i=1; i<len; i++){ // 预处理,得到所有前缀和值, sum[i] 表示 nums 数组中 [0, i] 元素的和
sum[i] = sum[i-1] + nums[i];
}
// 最精彩的部分 —— ([j , i]元素的和 == k) 可以转化为 (sum[i] - sum[j] == k) 可以转化为 (sum[i] - k == sum[j] )
// 因此我们只需要找出存在几个这样的 sum[k], 使得 (sum[i] - k == sum[j])
// 为了统计 sum[j] 的个数,我们使用 HashMap 进行记录 ,其 key 为 sum[j] 的值,value 为值相同的 sum[j] 的个数
Map<Integer, Integer> map = new HashMap<>();
map.put(0, 1); // 当前前缀和即等于 k —— sum[i] - k == 0
for(int i=0; i<nums.length; i++){
int temp = sum[i] - k;
res += map.getOrDefault(temp, 0); // 得到指定 sum[j] 数量
map.put(sum[i], map.getOrDefault(sum[i], 0) + 1); // 统计在 i 之前相同的 sum[j] 的个数
}
return res;
}