算法题9:和为 K 的子数组

题目描述:

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

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

 

示例 1:

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

示例 2:

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

 

思路:

开始会想到滑动窗口,但是发现不行,因为滑动窗口需要满足个特点就是单调性,即当右端点元素进入窗口时,窗口元素和是不能减少的,这里包含负数,当左端点往右侧移动,

右端点也往右移动时窗口内元素的和是不确定的,有可能增加也有可能减少,没办法拿到答案。
这里的思路是:前缀和,即s[0]=0, s[i+1]=nums[0]+nums[1]+⋯+nums[i]。

假设i<j,如果nums[i]到nums[j-1]的元素和等于k,那么前缀和就是s[j]-s[i]=k,遍历过的前缀和等于k的个数也就是题中该数组中和为子数组的个数

也可以写为s[i] = s[j] - k,以nums=[1,1,-1,1,-1],k=1为例,前缀和为s=[0,1,2,1,2,1]。
比如s[j] = 1,那么s[i]=s[j]-k = 1-1=0,那我们要找的就是前面遍历过的前缀和中有多少个0。

实现方法:在遍历s[j]的同时用字典(哈希表)cnt存储s[j]的个数。遍历到s[j]时,从字典中可以找到cnt[s[j]-k]个s[i],即为元素和等于k的子数组的个数,加入结果。

 python:

class Solution:
    def subarraySum(self, nums: List[int], k: int) -> int:
        res, s = 0, 0 # s表示前缀和
        cnt = defaultdict(int) # 统计s[j]的个数
        cnt[0] = 1 # s[0] = 0 单独统计
        for i in nums:
            s += i
            res += cnt[s - k]
            cnt[s] += 1
        return res

 

java:

class Solution {
    public int subarraySum(int[] nums, int k) {
        int res = 0;
        int s = 0;
        Map<Integer, Integer> cnt = new HashMap<>(nums.length + 1); // 设置容量
        cnt.put(0,1);  // s[0] = 0 单独统计
        for (int i : nums) {
            s += i;
            res += cnt.getOrDefault(s - k, 0);
            cnt.merge(s, 1, Integer::sum); // cnt[s] ++ 
        }
        return res;
        
    }
}

结果:

 

posted @ 2025-05-14 08:47  夏晓旭  阅读(15)  评论(0)    收藏  举报