给你一个整数数组 nums
和一个整数 k
,请你统计并返回 该数组中和为 k
的子数组的个数 。
子数组是数组中元素的连续非空序列。
示例 1:
输入:nums = [1,1,1], k = 2
输出:2
示例 2:
输入:nums = [1,2,3], k = 3
输出:2
提示:
1 <= nums.length <= 2 * 10^4
-1000 <= nums[i] <= 1000
-10^7 <= k <= 10^7
解题思路一
暴力解法:
两层循环,外层循环i遍历数组,内层循环向后一个一个累加,如果数值等于k则计数器加1。最终返回计数器。
代码:
class Solution {
public int subarraySum(int[] nums, int k) {
int len = nums.length,sum=0;
int count=0;
for(int i=0;i<len;i++){
sum=0;
for(int j=i;j<len;j++){
sum+=nums[j];
if(sum==k){
count++;
}
}
}
return count;
}
}
结果:
上述代码时间复杂度很清楚,两层循环,O(n^2),空间复杂度为O(1)。
解题思路二
首先得到每个元素位置处,从0-该位置之前所有元素的累计和sum[i]。那么以该位置结尾,和为k的子数组个数等于前面出现了sum[i]-k的累计和个数。例如元素数组为[1,2,3,4,5],k为5,则每个sum=[1,3,6,10,15]。假设现在遍历到了3,那么sum[3]-k=1,3之前出现子元素和为1只有一个,即nums[0-0],因此和为5的子数组为[1-2]。利用一个Map存储前面出现某个值的次数,即可每次在O(1)的时间复杂度得到前面和为sum-k的个数。也就是满足条件的子数组个数。
代码:
class Solution {
public int subarraySum(int[] nums, int k) {
int len = nums.length;
Integer pre=0;
int count=0;
Map<Integer,Integer> map = new HashMap<Integer,Integer>(); // 利用HashMap存储前缀和以及出现的个数
map.put(0,1);// 初始令前缀和为0,出现1次
for(int i=0;i<len;i++){
pre+=nums[i];// 从0-i所有元素的累加和
if(map.containsKey(pre-k)){ // 如果之前出现过pre-k,则以当前i为结尾的存在满足和为k的子数组
count+=map.get(pre-k);
}
if(map.containsKey(pre)){// 将当前累计和加入map中
map.put(pre,map.get(pre)+1);
}else{
map.put(pre,1);
}
}
return count;
}
}
总结
暴力解法很容易想到,枚举所有子数组的情况,计算子数组的值是否等于k即可。第二种方法的难点在于利用前0-i的元素和与k的差值在前面出现过几次。这里需要用到HashMap提升查找和修改效率。