题目描述
给整数数组 nums 和整数 k,求连续子数组的和等于 k 的个数(数组可能含负数)。
nums = [1, 2, 3]
k = 3
输出 = 2([1,2] 和 [3])
思路解析
双重循环把每段都加一遍能做,可一旦数据量大就吃力,而且没利用上「前缀和之差」这个巧劲。
记 pre 为从头加到当前的累加和。一段 (i, j] 的和等于 pre[j] − pre[i]。要它等于 k,就是找「前面有多少个 pre[i] 正好等于 pre[j] − k」。
边走边把出现过的前缀和计数存进哈希表。到每个位置,查一下 pre − k 出现过几次,就知道有几段以这里结尾、和为 k。初始要放一个 {0: 1}。
初始化:开局先往哈希表塞一个 {0:1}。它代表「啥都没加」的前缀和 0——这是为了能数到「从头开始那一段」。
i=0, 值 1:加到 1,前缀和 pre=1。查 1−3=−2 在不在表里——不在,没有合格子数组。然后把 pre=1 记进哈希表。
i=1, 值 2:加到 2,pre=3。查 3−3=0,表里有 1 个 0!说明从某处到这里和正好是 3——正是 [1,2]。count 加 1。
i=2, 值 3:加到 3,pre=6。查 6−3=3,表里有 1 个 3!对应子数组 [3],count 再加 1。扫完,答案是 2。
「连续子数组求和/计数」的通杀套路:把区间和转成前缀和之差,再用哈希表 O(1) 查有没有需要的那个前缀和。
参考代码(Python)
Python
1def subarraySum(nums, k):
2 from collections import defaultdict
3 seen = defaultdict(int); seen[0] = 1 # 关键:和为 0 出现 1 次
4 pre = 0; count = 0
5 for x in nums:
6 pre += x
7 count += seen[pre - k] # 先查:有几个前缀和 = pre-k
8 seen[pre] += 1 # 后记:把当前前缀和计数
9 return count复杂度分析
- 时间复杂度:O(n) —— 一遍扫描
- 空间复杂度:O(n) —— 哈希表存前缀和
套路模板
骨架就这几行。两个铁律:哈希表初始化 {0:1}、先查 pre−k 再记 pre。顺序反了答案就错。
Python
1seen = {0: 1} # 别忘了这一项
2pre = count = 0
3for x in nums:
4 pre += x
5 count += seen.get(pre - k, 0) # 先查
6 seen[pre] = seen.get(pre, 0) + 1 # 后记易错点
- ❌ 忘了初始化 seen = {0: 1} → ✅ 一开始就放 {0: 1}(少了它,「从下标 0 开头、整段和为 k」的情况会被漏掉)
- ❌ 查 k − pre → ✅ 查 pre − k(区间和 = pre[j] − pre[i] = k,所以要找的是 pre[i] = pre[j] − k)
- ❌ 先记 pre 再查(顺序反了) → ✅ 先查 pre−k,再记 pre(反了会把当前这个前缀和也算进候选,凭空多算)
以上文字与上方动画完全同一思路:先看动画建立直觉 → 读文字巩固 → 关掉页面自己默写一遍代码,能写出来才算真的掌握。