文章目录
- [🪐1. 题目](#🪐1. 题目)
- [🌟2. 算法原理](#🌟2. 算法原理)
- [🌞3. 代码实现](#🌞3. 代码实现)
🪐1. 题目
给你一个整数数组 nums
和一个整数 k
,请你统计并返回 该数组中和为 k
的子数组的个数 。
子数组是数组中元素的连续非空序列。
示例 1:
cpp
输入:nums = [1,1,1], k = 2
输出:2
示例 2:
cpp
输入:nums = [1,2,3], k = 3
输出:2
提示:
1 <= nums.length <= 2 * 104
-1000 <= nums[i] <= 1000
-107 <= k <= 107
🌟2. 算法原理
这题要找的是连续的子数组,例如:
⭐解法一:暴力枚举
固定一个位置,找到一个符合条件的ret
就++
,当然了,这里还不能停,继续往后变量,直到数组结尾处,这个时间复杂度为O(n^2^)。
⭐解法二:前缀和+哈希表
在暴力枚举的过程中,这里固定的位置和往后遍历,虽然两个指针方向是一样的,但是不能使用滑动窗口,因为这个数组里面有负数 和
0
,并不是单调递增或者单调递减的,这样就会导致漏掉一些情况。
我们固定一个位置,往前遍历它的所有子数组,也可以理解为固定一个i
位置,以i
为结尾的所有子数组。
当枚举到i
位置的时候,想找和为k
的子数组就可以转换为在[0,i-1]
区间内,有多少个前缀和为dp[i] - k
的子数组。
我们把前缀和数组处理出来之后,要找
i
位置之前有多少个和为dp[i]-k
,不能又从头到尾遍历,这样的时间复杂度还不如暴力求解
因此我们可以用哈希表 ,来快速找到在i
之前有多少个前缀和是等于dp[i]-k
的。那么哈希表里面的映射关系是前缀和 和出现的次数 。
细节问题:
- 前缀和加入哈希表的时机:不能一股脑将前缀和全部丢进哈希表,我们只需要计算在
i
位置之前,哈希表里面只保存[0,i-1]
位置的前缀和。- 不用真正的创建一个前缀和数组,只需借助一个临时变量,记录这个位置之前的前缀和,计算完毕之后,再更新一下这个临时变量。
- 当整个前缀和为
k
时,我们需要在哈希表中默认有一个前缀和为0
的位置hash[0]=1
,以防这个情况被忽略。
此算法的时间复杂度为O(n)。
🌞3. 代码实现
cpp
class Solution {
public:
int subarraySum(vector<int>& nums, int k)
{
unordered_map<int,int> hash;
hash[0] = 1;
int dp = 0;
int ret = 0;
for(auto e:nums)
{
dp+=e; //当前位置的前缀和
if(hash.count(dp-k)) //在这个位置之前,有多少个前缀和为dp-k的
ret+=hash[dp-k];
hash[dp]++;
}
return ret;
}
};