给你一个整数数组 nums 和一个整数 k ,请你统计并返回 *该数组中和为 k ***的子数组的个数 。
子数组是数组中元素的连续非空序列。
示例 1:
输入: nums = [1,1,1], k = 2
输出: 2
示例 2:
输入: nums = [1,2,3], k = 3
输出: 2
枚举法——时间复杂度大
/**
* @param {number[]} nums
* @param {number} k
* @return {number}
*/
var subarraySum = function(nums, k) {
let count=0
for(let i=0;i<nums.length;i++){
let sum=0
for(let j=i;j<nums.length;j++){
sum+=nums[j]
}
if(sum===k){
count++
}
}
return count
}
前缀和+哈希表优化
方法一的瓶颈是「每次都要枚举左端点」。我们可以用 前缀和 来优化。
我们定义 pre[i] 为 [0,..,i] 里所有数的和。那么子数组[j,..,i]的和为sum(j..i) = pre[i] - pre[j-1]。所以我们知道有多少个前缀和为pre[i]-k,就知道有多少个满足条件的子数组。
我们用一个哈希表 mp 来存储 前缀和出现的次数:
- key:某个前缀和
- value:该前缀和出现的次数
遍历数组时:
- 计算当前前缀和
pre。 - 判断
pre - k是否在哈希表中,如果有,说明存在若干个子数组满足条件,把它们计入答案。 - 更新
mp[pre],记录这个前缀和出现的次数。
/**
* @param {number[]} nums
* @param {number} k
* @return {number}
*/
var subarraySum = function(nums, k) {
const map=new Map()
map.set(0,1)
let count=0,pre=0
for(const x of nums){
pre+=x
if(map.has(pre-k)){
count+=map.get(pre-k)
}
if(map.has(pre)){
map.set(pre,map.get(pre)+1)
} else {
map.set(pre,1)
}
}
return count
}