Q560 和为 K 的子数组

题目:给你一个整数数组 nums 和一个整数 k,请你统计并返回该数组中和为 k 的连续子数组的个数。

题目要求相邻子数组的和等于 K,前缀和天然适合处理区间和:

$$preSum[r] - preSum[l] = K \iff preSum[l] = preSum[r] - K$$

要从大量子数组中快速找出满足条件的数量。暴力双 for 循环复杂度 $O(n^2)$,哈希表可以降到 $O(n)$。

哈希表的核心作用:空间换时间——当题目既要计算子数组情况,又要从子数组中快速查找满足条件的项时,哈希表是首选工具。

场景哈希表记录
求数量(本题)val → count(前缀和 → 出现次数)
求最大/最小长度val → index(前缀和 → 首次下标)
  1. 遍历数组,维护当前前缀和 sum
  2. 计算 need = sum - K,在哈希表中查找 need 出现的次数,累加到结果
  3. 将当前 sum 存入哈希表(次数 +1)
  4. 关键初始化:map[0] = 1,处理从数组开头就满足条件的子数组
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#include <vector>
#include <unordered_map>
using namespace std;

class Solution {
public:
    int subarraySum(vector<int>& nums, int k) {
        unordered_map<int, int> valToCount; // 前缀和 → 出现次数
        valToCount[0] = 1;                  // 边界初始化
        int sum = 0, res = 0;

        for (int num : nums) {
            sum += num;                     // 累加当前前缀和
            int need = sum - k;
            if (valToCount.find(need) != valToCount.end()) // 此处为满足需求
            {
                res += valToCount[need];
            }
            valToCount[sum]++;
        }
        return res;
    }
};
  • 时间复杂度:$O(n)$,一次遍历
  • 空间复杂度:$O(n)$,哈希表最坏存 n 个不同前缀和

原代码先构造完整 preSum 数组再遍历,多了一次 $O(n)$ 的预处理。改为一边累加一边查哈希表,只扫一遍。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
int subarraySum(vector<int>& nums, int k) {
    unordered_map<int, int> mp;
    mp[0] = 1;
    int sum = 0, ans = 0;
    for (int x : nums) {
        sum += x;
        if (mp.count(sum - k)) ans += mp[sum - k];
        mp[sum]++;
    }
    return ans;
}
题目转化条件哈希表记录
LeetCode 560 和为 K 的子数组pre[r] - pre[l] = K前缀和 → 出现次数
LeetCode 525 连续数组pre[r] = pre[l](0→-1, 1→1)前缀和 → 首次下标
LeetCode 974 和可被 K 整除同余前缀和配对余数 → 出现次数

通用套路:前缀和做差转化区间条件 → 哈希表快速查找 → 根据题目要求决定存次数还是存下标。

  • valToCount[0] = 1 不能漏:当 sum == k 时,need = 0,此时应计为一种合法方案。
  • 先查哈希表再更新:避免 k = 0 时当前前缀和自己匹配(空子数组不算)。

相关内容