给你一个整数数组 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提升查找和修改效率。










