前缀和技巧实战:和为 K 的子数组与和可被 K 整除的子数组
在算法面试中,子数组求和问题非常常见。如果直接暴力枚举所有子数组,时间复杂度会达到 O(n²) 甚至更高。利用前缀和(Prefix Sum)结合哈希表,我们可以将这类问题的时间复杂度优化至 O(n)。本文将通过 LeetCode 560 和 974 两道经典题目,深入讲解这一核心技巧。
560. 和为 K 的子数组
题目链接: LeetCode 560
题目描述: 给定一个整数数组和一个整数 k,你需要找到该数组中和为 k 的连续子数组的个数。

解题思路:
设 sum[i] 表示从索引 0 到 i 的所有元素之和。如果我们想知道以位置 i 结尾、和为 k 的子数组有多少个,本质上就是寻找有多少个起始位置 x,使得 sum[i] - sum[x-1] = k。移项后得到 sum[x-1] = sum[i] - k。
这意味着,我们不需要维护一个完整的前缀和数组,只需要在遍历过程中,用一个哈希表记录当前前缀和出现的次数。对于每一个当前位置的前缀和 currentSum,我们只需查询哈希表中是否存在 currentSum - k,如果存在,说明之前有若干次前缀和满足条件,累加其出现次数即可。
注意初始化时 hash[0] = 1,这代表前缀和为 0 的情况(即从数组开头开始的子数组)。
参考实现:
class Solution {
public:
int subarraySum(vector<int>& nums, int k) {
unordered_map<int, int> hash;
hash[0] = 1; // 初始化,处理从下标 0 开始的子数组
int n = nums.size();
int sum = 0, ret = 0;
for (auto x : nums) {
sum += x;
if (hash.count(sum - k)) {
ret += hash[sum - k];
}
hash[sum]++;
}
ret;
}
};



