🧠 引言
这是一个经常被忽视却极具含金量的算法思维题:
给定整数数组
nums和整数k,求 和为 k 的连续子数组个数。
你可能第一反应是暴力枚举,但这题的高效解法——前缀和 + 哈希表——不仅性能飞跃,而且思维模型在很多算法场景中反复出现!
🧪 题目:Subarray Sum Equals K(LeetCode 560)
❓题目描述
输入: nums = [1,1,1], k = 2
输出: 2 ([1,1] 出现两次)
❌ 暴力解法(时间复杂度 O(n²))
let count = 0;
for (let i = 0; i < nums.length; i++) {
let sum = 0;
for (let j = i; j < nums.length; j++) {
sum += nums[j];
if (sum === k) count++;
}
}
⛔ 对于大数组会超时,我们需要更高效的方式!
⚡ 解法核心:前缀和 + 哈希表
核心公式:
若前缀和
preSum[i] - preSum[j] === k,说明nums[j...i-1]是一个合法子数组
可转化为:
当前前缀和 preSum
我们想找有没有 preSum - k 出现过
于是我们用哈希表记录每个前缀和出现的次数。
💻 JavaScript 实现(O(n))
function subarraySum(nums, k) {
const map = new Map();
map.set(0, 1); // 预处理前缀和为 0 的情况
let preSum = 0, count = 0;
for (let num of nums) {
preSum += num;
if (map.has(preSum - k)) {
count += map.get(preSum - k);
}
map.set(preSum, (map.get(preSum) || 0) + 1);
}
return count;
}
🐍 Python 实现(O(n))
from collections import defaultdict
def subarray_sum(nums, k):
pre_sum_count = defaultdict(int)
pre_sum_count[0] = 1
pre_sum = 0
count = 0
for num in nums:
pre_sum += num
count += pre_sum_count[pre_sum - k]
pre_sum_count[pre_sum] += 1
return count
✅ 示例运行(JS / Python 同样适用)
输入:nums = [1,2,3], k = 3
前缀和序列:1, 3, 6
检查是否存在 preSum - k:
- 1-3=-2 ❌
- 3-3=0 ✅
- 6-3=3 ✅
输出:2
⚠️ 易错点总结
| 错误点 | 正确写法 |
|---|---|
没初始化 map[0]=1 | 要考虑前缀和本身就等于 k 的情况 |
用 map.set/get 写错判断 | JS 建议使用 `(get() |
| Python defaultdict 没用 | 用 defaultdict(int) 避免 KeyError |
🧩 拓展任务
- 输出所有满足条件的子数组起止索引
- 支持负数和浮点数(注意精度误差)
- 拓展为二维版本(矩阵区域和为 k)
📚 总结一句话
前缀和 + 哈希表 是数组求和类问题的“超级解法”,一旦掌握,数组题性能直接飞升!
📘 下一篇预告:
第17篇:【滑动窗口进阶】最小覆盖子串(困难)+ 字符计数模板封装(JS + Python 双解)