题目链接:和为K的子数组
题目描述:
给你一个整数数组 nums
和一个整数 k
,请你统计并返回该数组中和为 k
的连续子数组的个数。
题目思路:
方法一:暴力解法O(n^2) 超时
var subarraySum = function(nums, k) {
const length=nums.length;
let num=0;
for(let i=0;i<length;i++){
let sum=nums[i];
if(sum===k)
num++;
for(let j=i+1;j<length;j++){
sum+=nums[j];
if(sum===k)
num++;
}
}
return num;
};
方法二:前缀和O(n^2) 超时
var subarraySum = function(nums, k) {
const length=nums.length;
let num=0;
const dp=new Array(length+1);
dp[0]=0;
// 数组前缀和
for(let i=0;i<length;i++){
dp[i+1]=dp[i]+nums[i];
}
for(let i=1;i<=length;i++){
for(let j=i;j<=length;j++){
if(dp[j]-dp[i-1]===k)
num++;
}
}
return num;
};
方法三:前缀和+哈希表优化 时间O(n) 空间O(n)
分析:求连续和为k的子数组的个数,即求出以每一个nums[i]为区间结尾的和为k的子数组的个数之和。
设前缀数组prev,prev[i]为[0,i]的元素之和 ,
若0<=j<=i,prev[i]-prev[j]===k,则从[j+1,i]区间的子数组和为k。
所以要求连续和为k的子数组的个数,就要求出prev[i]-prev[j]===k的个数,即求出prev[i]-k===prev[j]的个数。其中0<=j<=i。
所以要求出以i为区间结尾的且子数组和为k的子数组的个数,需要找出其左边前缀和为prev[i]-k的前缀和个数。
定义一个map,key为数组的前缀和,value为该前缀和出现的次数,从左往右遍历,对于每一个prev[i],prev[i]-k出现的次数,就是以i为区间结尾的且和为k的子数组的个数。
由于prev[i]=prev[i-1]+nums[i],只和前面一个元素有关,所以不需要用数组,只用一个prev变量表示前缀和即可。
// 前缀和+哈希表优化
var subarraySum = function(nums, k) {
const map=new Map();
let count=0,prev=0;
map.set(0,1);
for (const item of nums) {
prev+=item;
if(map.has(prev-k)){
count+=map.get(prev-k);
}
if(map.has(prev)){
map.set(prev,map.get(prev)+1);
}else
map.set(prev,1);
}
return count;
};