算法题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; } }
结果:


浙公网安备 33010602011771号