一.题目要求
给你一个整数数组 nums 和一个整数 k ,请你统计并返回 该数组中和为 k 的子数组的个数 。
子数组是数组中元素的连续非空序列。
二.题目难度
中等
三.输入样例
示例 1:
输入:nums = [1,1,1], k = 2
输出:2
示例 2:
输入:nums = [1,2,3], k = 3
输出:2
提示:
1 <= nums.length <= 2 ∗ 1 0 4 2 * 10^4 2∗104
-1000 <= nums[i] <= 1000
− 1 0 7 -10^7 −107 <= k <= 1 0 7 10^7 107
四.解题思路
解法1:暴力穷举
解法2:前缀和 + 哈希表
优化思路
前缀和(Prefix Sum):这是一种常用的技巧,可以快速计算任意区间内的元素和。我们遍历数组,计算从第一个元素到当前元素的总和,并将这个总和保存起来作为前缀和。
哈希表(Hash Map):利用哈希表存储不同前缀和出现的次数。这样,对于每一个新的前缀和,我们可以在常数时间内查询哈希表找到之前有多少个前缀和等于当前前缀和减去k。这是因为如果存在一个旧的前缀和等于当前前缀和减去k,那么从那个旧的前缀到当前前缀的子数组和就为k。
五.代码实现
解法1
cpp
class Solution {
public:
int subarraySum(vector<int>& nums, int k) {
int result = 0;
int tmp;
//0 1 2 0 -1 2 0 3
int i, j;
for (vector<int>::iterator it = nums.begin(); it != nums.end(); it++)
{
tmp = 0;
for (vector<int>::iterator itt = it; itt != nums.end(); itt++)
{
tmp += *itt;
if (tmp == k)
{
result++;
}
}
}
return result;
}
};
解法2
c
class Solution {
public:
int subarraySum(vector<int>& nums, int k) {
int count = 0, sum = 0;
unordered_map<int, int> prefixSumCount;
prefixSumCount[0] = 1; // 初始化,和为0的前缀和出现一次
for (int num : nums) {
sum += num; // 计算当前前缀和
// 检查是否存在一个旧的前缀和,使得旧的前缀和与当前前缀和之间的差等于k
if (prefixSumCount.count(sum - k)) {
count += prefixSumCount[sum - k];
}
// 更新当前前缀和的计数
prefixSumCount[sum]++;
}
return count;
}
};
六.题目总结
哈希表+前缀和的技术通常用于解决涉及数组或序列累积和的问题,特别是当需要快速查询子数组或子序列和的场合。这种方法特别适用于以下几类问题:
- 连续子数组的和:最常见的应用是找出数组中和为给定值的连续子数组数量。前缀和可以快速计算任意(i, j]区间的和,而哈希表用来存储各个前缀和出现的次数,从而在O(1)时间内判断是否存在某个特定的前缀和值。
- 求和等于特定值的子数组数量:如上例所示,通过计算当前前缀和并查询哈希表中是否存在一个或多个前缀和,使得当前前缀和与这些前缀和之差等于目标值,可以快速找到子数组和为特定值的数组数量。
- 数组的子区间问题:对于求解数组中满足特定条件的子区间(如和为k的子数组)数量问题,可以通过维护一个前缀和的哈希表来优化查找过程,从而避免使用双重循环,实现更高效的算法。
- 处理多次查询:当面对多次查询数组区间和的问题时,前缀和结合哈希表可以大大减少重复计算,每次查询可以在O(1)时间内完成,特别是当数组不经常变化而查询非常频繁时。
- 寻找具有特定和的子矩阵:在二维数组(如矩阵)中,前缀和技术可以扩展到二维,用于快速计算任意子矩阵的和,结合哈希表可以用来解决特定和的子矩阵问题。
使用哈希表加前缀和的方法,可以将一些原本时间复杂度较高的问题转化为线性时间复杂度处理,从而大幅提升算法效率。这种方法的关键优势在于它通过空间换时间,利用哈希表来快速访问和更新前缀和信息,避免了冗余的计算和遍历。