力扣 560.和为k的子数组

题目描述:

给你一个整数数组 nums 和一个整数 k ,请你统计并返回 该数组中和为 k 的子数组的个数 

子数组是数组中元素的连续非空序列。

 

示例 1:

输入:nums = [1,1,1], k = 2
输出:2

示例 2:

输入:nums = [1,2,3], k = 3
输出:2

 

提示:

  • 1 <= nums.length <= 2 * 104
  • -1000 <= nums[i] <= 1000
  • -107 <= k <= 107

题解:这道题一开始看到子数组,就想用滑动窗口来解决,但是遗憾的是,无法判断什么时候进行窗口的缩小,因为数组中的元素有负值,当前窗口相加的值不等于k可能是因为窗口中元素太小,需要通过缩小窗口来不断剔除小元素,但是还有一种窗口相加不等于k的原因是现在元素个数不够,那么就需要继续扩大窗口,由此产生了矛盾,也就是说无法判断当前窗口中值不等于k是因为元素个数太少了,还是窗口内的元素太小了,因此滑动窗口解决不了。

考虑使用暴力加剪枝的方法,暴力方法很容易想到,就是遍历的时候,每次以当前遍历的元素打头或者收尾,寻找子数组,如果选择以当前元素作为子数组的尾元素,那么就需要内层for循环控制当前元素和当前位置之前的一个、两个、三个元素构成子数组,计算每个子数组的值,等于k就记录,不等于k内层for循环就继续向前圈入元素,组成更长的子数组,直到圈到第一个元素。至此,当前位置作为子数组尾元素的情况判断结束,外层循环继续下一个元素的判断。

存在的问题,会产生很多无用的循环计算,比如已经在外层循环下标是4的时候计算过nums[2]+nums[3],但是外层循环遍历到5时,还会有计算一次,造成无用计算,因此考虑可以使用前缀和,这样一次遍历就能计算出来任意连续的数组元素之和。

有了前缀和,还需要用来解决当前的问题,要找一个子数组和为k,就是要找j满足外层循环遍历到i位置的前缀和pre[i]减去内层循环遍历到j位置的前缀和pre[j-1]等于k,即pre[i]-pre[j-1]=k,此时子数组的下标范围:[j,i],因此考虑如何在遍历到i位置的时候,找到一个下标j满足pre[i]-k = pre[j-1],我们说,pre[j-1]的值在循环的时候肯定比pre[i]的值先计算出来(因为此时我们仍然假设i作为子数组的尾元素,j是内层循环i之前的一个元素下标),因此,可以在计算出pre[j-1]的时候,就先将其存入map中,key=pre[j-1],value值存储和为pre[j-1]的值出现了几次,这样每当pre[j-1]满足了一次pre[i]-pre[j-1]=k的要求,就取出pre[j-1]对应的value值累加到count(记录最终返回的结果)中,也就是说i位置之前多少个位置的j满足pre[i]-pre[j-1]=k,也就是以i为子数组尾巴,能够有多少个满足要求的子数组。

 

实现代码: 

class Solution {
    public int subarraySum(int[] nums, int k) {
        int count = 0,pre = 0;
        HashMap<Integer,Integer> mp = new HashMap<>();
        mp.put(0,1);
        for(int i  =0;i<nums.length;i++){
            pre+=nums[i];
            if(mp.containsKey(pre-k)){
                count+=mp.get(pre-k);
            }
            mp.put(pre,mp.getOrDefault(pre,0)+1);
        }
        return count;
    }
}

 

 

  • 9
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值