一.题目描述
二.题目解析
找一段连续的子区间,该子区间的和为k。
这道题无法使用滑动窗口解决。因为当找到一种结果之后,right还应该继续向后走,后面的数组可能还会有结果。
当right走到了结尾之后,此时left++,right又要回到left的位置重新遍历,所以left和right并不是向同一方向移动,所以不能使用滑动窗口解决问题。
三.算法原理
1.暴力解法
暴力解法就是枚举出每一个子数组,记录和为k的子数组即可。遇到一个和为k的子数组后,应继续向后遍历,避免遗漏一些结果。遍历完一遍之后,从下一个位置开始继续遍历。
时间复杂度:两层for循环,即可枚举出所有的子数组,所以时间复杂度为O(n^2)
空间复杂度:只是用了有限的变量,所以空间复杂度为O(1)
2.前缀和
我们先理解一个概念——以i为结尾的子数组:
以i为结果的子数组且和为k的话, 我们就可以找到前缀和与k之前的关系。
我们将数组分为两部分,一部分是以i为结尾的子区间,另一部分是x区间,而这两个区间之和就是dp[i],所以满足k+x = dp[i]。所以x = dp[i] - k.
所以如果以i为结尾的子数组和为k的话,则x子数组一定满足dp[i]-k。所以我们在枚举出一个位置之后,只要判断有多少个和为dp[i] - k的子数组即可。
所以我们需要借助一个哈希表,将前缀和存在里面,我们只需要在里面找到有几个和为dp[i]-k的即可。
细节处理:因为[0,i]这个数组的和就可能是k,而此时就不存在x数组,所以我们在哈希表中需要提前存一个(0,1)。
时间复杂度分析:我们不需要真的先求出前缀和数组,然后再放进去。我们求i位置的子数组,只会用到[0,i-1]这个区间的前缀和。所以我们可以边统计个数,便计算前缀和。所以时间复杂度为O(n)
空间复杂度:我们需要用哈希表存储前缀和的出现次数,所以需要额外开辟空间,空间复杂度为O(n).
四.代码实现
// C++
class Solution
{
public:
int subarraySum(vector<int>& nums, int k)
{
unordered_map<int,int> mp;
mp[0] = 1;
int ret = 0, sum = 0;
for(auto e : nums)
{
sum += e;
if(mp.count(sum-k)) ret += mp[sum-k];
mp[sum]++;
}
return ret;
}
};
# python
class Solution:
def subarraySum(self, nums: List[int], k: int) -> int:
mp = defaultdict(int) # 创建一个默认值为0的字典
mp[0] = 1 # 初始化前缀和为0的计数为1,用于处理数组开头的元素
ret = 0 # 存储找到的对数
sum_ = 0 # 当前的前缀和
for e in nums:
sum_ += e # 计算当前的前缀和
if sum_ - k in mp:
ret += mp[sum_ - k] # 如果存在这样的前缀和,则增加对数
mp[sum_] += 1 # 更新当前前缀和的计数
return ret