【前缀和 + 哈希】一行代码也能解“和为 K 的子数组个数”(JS + Python 双解)

57 阅读2分钟

🧠 引言

这是一个经常被忽视却极具含金量的算法思维题:

给定整数数组 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 双解)