560. 和为 K 的子数组 -Medium
提示
给你一个整数数组 nums 和一个整数 k ,请你统计并返回 该数组中和为 k * **的子数组的个数 *。
子数组是数组中元素的连续非空序列。
示例 1:
输入:nums = [1,1,1], k = 2
输出:2
示例 2:
输入:nums = [1,2,3], k = 3
输出:2
一步步详细解答:前缀和 + 哈希表方法
1. 问题理解 我们需要统计数组中连续子数组的和等于 k 的个数。例如:
nums = [1,1,1], k = 2 → 输出 2(因为 [1,1] 和 [1,1] 都满足)
nums = [1,2,3], k = 3 → 输出 2(因为 [1,2] 和 [3] 都满足)
2. 暴力解法(O(n²)) 最直观的方法是双重循环 ,计算所有可能的子数组的和:
1 2 3 4 5 6 7 8 9 10 11 12 13 var subarraySum = function (nums, k ) { let count = 0 ; for (let start = 0 ; start < nums.length ; start++) { let sum = 0 ; for (let end = start; end < nums.length ; end++) { sum += nums[end]; if (sum === k) { count++; } } } return count; };
缺点 :当 nums 很大时,效率很低(比如 nums.length = 10^5 时,计算量会非常大)。
3. 优化解法:前缀和 + 哈希表(O(n)) 为了优化,我们引入**前缀和(Prefix Sum)**的概念。
什么是前缀和? 前缀和是指从数组开头到当前位置的所有元素的和。例如:
nums = [1, 2, 3] 的前缀和数组是 [0, 1, 3, 6](初始 0 是为了方便计算)。
为什么用前缀和? 假设我们有一个子数组 nums[i..j],它的和可以表示为:
sum(nums[i..j]) = prefixSum[j+1] - prefixSum[i]
如果我们希望 sum(nums[i..j]) = k,那么:
prefixSum[j+1] - prefixSum[i] = k
=> prefixSum[j+1] - k = prefixSum[i]
也就是说,**如果 prefixSum[j+1] - k 在之前的前缀和中出现过,就说明存在一个子数组的和等于 k**。
如何用哈希表优化? 我们可以用一个哈希表 map 存储前缀和出现的次数 ,这样可以在 O(1) 时间内查询 prefixSum[j+1] - k 是否出现过。
代码实现 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 var subarraySum = function (nums, k ) { let count = 0 ; let sum = 0 ; const map = new Map (); map.set (0 , 1 ); for (let i = 0 ; i < nums.length ; i++) { sum += nums[i]; if (map.has (sum - k)) { count += map.get (sum - k); } map.set (sum, (map.get (sum) || 0 ) + 1 ); } return count; };
示例解析 例子 1 :nums = [1,1,1], k = 2
i
nums[i]
sum (prefixSum)
sum - k
map (sum → count)
count
-
-
0
-
{0:1}
0
0
1
1
1-2=-1
{0:1, 1:1}
0
1
1
2
2-2=0
{0:1, 1:1, 2:1}
1
2
1
3
3-2=1
{0:1, 1:1, 2:1, 3:1}
2
**最终 count = 2**,符合预期。
例子 2 :nums = [1,2,3], k = 3
i
nums[i]
sum (prefixSum)
sum - k
map (sum → count)
count
-
-
0
-
{0:1}
0
0
1
1
1-3=-2
{0:1, 1:1}
0
1
2
3
3-3=0
{0:1, 1:1, 3:1}
1
2
3
6
6-3=3
{0:1, 1:1, 3:1, 6:1}
2
**最终 count = 2**,符合预期。
4. 关键点总结
**map.set(0, 1)**:处理 sum = k 的情况(子数组从 nums[0] 开始)。
sum - k 检查 :如果 sum - k 在 map 中,说明存在子数组和为 k。
**更新 map**:记录所有 sum 的出现次数,以便后续查询。
5. 类似题目
560. 和为 K 的子数组 (本题)
523. 连续的子数组和 (和可被 K 整除)
325. 和等于 k 的最长子数组 (求最长长度)
最终结论
前缀和 + 哈希表 是解决子数组和问题 的最优方法(O(n) 时间)。
核心思想 :sum[j] - sum[i] = k ⇒ sum[j] - k = sum[i],用哈希表记录 sum[i] 的出现次数。
适用场景 :求子数组和、最长/最短子数组长度等问题。