题目
思路
暴力法
本题求和为k的子数组的个数,一个直观的想法是:
- 枚举每个子数组
- 计算每个子数组的和
- 统计和等于k的子数组的个数
但这样时间复杂度为O(n ^ 3)。当然,可以先求出前缀和数组,这样时间复杂度可以降为O(n ^ 2)
前缀和 + 枚举每个结尾
下面换一种思路,在从头到尾逐个扫描数组中的数字时求出前i
个数字之和,并且将和保存下来。数组的前i个数字之和记为x
。如果存在一个j(j<i)
,数组的前j个数字之和为x-k
,那么数组中从第i+1
个数字开始到第j
个数字结束的子数组之和为k
当我们遍历到下标为i的数字时,将i加入到前缀和x中,由于之前已经将以0开头,以0到i-1为结尾的子数字的和加到Map中,key为和,value为个数
因此要计算以i为结尾的子数组中有多少个其和为k的就好办了,就是Map中key为x - k的value
正确性证明
因为我们计算了以每一个下标作为子数组结尾的情况,因此不会漏掉任何一个子数组,结果是正确的
代码
public int subarraySum(int[] nums, int k) {
Map<Integer,Integer> map = new HashMap<>();
map.put(0,1);
int res = 0;
int sum = 0;
for (int num : nums) {
sum += num;
// 计算之前有多少个以0开头的子数组和为sum - k
res += map.getOrDefault(sum - k,0);
// 将以0开头,当前位置结尾的子数组和加到map中
map.put(sum,map.getOrDefault(sum,0) + 1);
}
return res;
}