题目描述
给定一个整数数组和一个整数 k,你需要找到该数组中和为 k 的连续的子数组的个数。
示例:
输入:nums = [1,1,1], k = 2
输出: 2 , [1,1] 与 [1,1] 为两种不同的情况。
解题思路
这个题和《剑指offer》第57题不一样,不能用一样的思路,因为根据题意我们不能对nums
数组排序。其实这个题和之前的“几数之和”的题目类似。
-
暴力法:以每个元素为起始点遍历扫描,检查子数组和是否满足情况。时间复杂度为 O ( n 2 ) O(n^2) O(n2),空间复杂度为 O ( 1 ) O(1) O(1)。
-
前缀和/累加和(动图):用
sum[i]
表示从0
到i
所有元素的和,那么nums[1] + nums[2] = sum[2] - sum[0]
。这里的sum[i]
实际上就是一种前缀和的思路,只需要遍历一次就可以知道所有的前缀和,存到map
里,用的时候就可以实现在常数时间的查找。我们在遍历数组的时候,依次在哈希表中存储
(sum[i],sum[i]的出现次数)
,当我们遍历到位置i
时,如果我们想找所有以i
位置为结尾的“和为 k 的连续的子数组的个数”时,直接去哈希表中找umap[curSum - k]
即可。(res += umap.count(curSum - k) == 0? 0: umap[curSum - k]
)时间度复杂度 O ( n ) O(n) O(n),空间复杂度 O ( n ) O(n) O(n)。
注:
umap[0] = 1; // 必须要提前加上,防止漏掉数组的前缀和刚好等于k的情况
参考代码
class Solution {
public:
int subarraySum(vector<int>& nums, int k) {
int length = nums.size();
if(length == 0)
return 0;
unordered_map<int, int> umap;
// 注意这里前缀和多了一个0,防止漏掉当前整个数组的前缀和刚好等于k的情况
umap[0] = 1; // 用前缀和的思路,必须要提前加上
int curSum = 0, res = 0;
for(auto num: nums){
curSum += num;
res += umap.count(curSum - k) == 0? 0: umap[curSum - k]; // 必须先更新res,再更新umap
umap[curSum]++; // 当前位置umap(哈希表)值的更新一般写在后面
}
return res;
}
};