前缀和——和为k的子数组

一.题目描述

560. 和为 K 的子数组 - 力扣(LeetCode)

二.题目解析

找一段连续的子区间,该子区间的和为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
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值