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 | var subarraySum = function(nums, k) { |
缺点:当 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 | var subarraySum = function(nums, k) { |
示例解析
例子 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]的出现次数。 - 适用场景:求子数组和、最长/最短子数组长度等问题。