560.和为K的子数组

128 篇文章 0 订阅

给定一个整数数组和一个整数 k,你需要找到该数组中和为 k 的连续的子数组的个数。

示例 1 

输入:nums = [1,1,1], k = 2
输出: 2 , [1,1] 与 [1,1] 为两种不同的情况。
说明 :

数组的长度为 [1, 20,000]。
数组中元素的范围是 [-1000, 1000] ,且整数 k 的范围是 [-1e7, 1e7]。

思路:看到这个首先想到用滑动窗口,但是最后还是没写出来,那就双层循环遍历呗,列举所有可能性,连续的前n项和等于k那子数组的个数就加1;这个方法的问题就在于:超出时间限制。说实话以前写算法没太考虑过时间限制,现在经常出现。那就说明时间复杂度太高了呗,这个暴力方法的时间复杂度是O(n²);那么就想办法怎么去降低这个时间复杂度。如果出现一个O(n)的算法就完全可以了;这个时候采用前缀和+哈希法。那么问题来了,什么是前缀和呢?

前缀和:从第0项到该项的和,举个栗子

0项到X项之和

F[x] = nums[0]+nums[1]+.........+nums[x]

num[x] = F[x]-F[x-1]

所以nums[i]到nums[j]总和:num[i]+....+nums[j] = F[j]-F[i-1]

所以这个题目可以转化成这样:F[j]-F[i-1]==k

当然这个题目转化成这样还是不性,为啥呢?因为有i和j。还是需要进行两层循环的,时间复杂度还没有变,看来前缀和势单力薄,还差点火候,那么此时上我的哈希。哈希在很多很多地方都有应用,之后写篇文章专门介绍哈希。其实在这里面我们根本不需要关注坐标,我们只在乎前缀和的值和出现的次数,这个时候建立哈希表,前缀和是键,出现的次数是值。其实就是看有多少前缀和是F[j]-k.最终就有几种情况。那么哈希表的初始话肯定是{0:1}.表示前缀和为0的出现了1次。

算法流程:

初始化哈希表(键:前缀和;值:出现次数)

遍历列表,计算前缀和,如果前缀和之前出现,加1就可;如果没出现那么就添加到哈希列表中去。

如果(前缀和-k)在哈希列表中,那么给计数器加1

此方法的时间复杂度和空间复杂度都是O(n),非常典型的一个空间换时间的一个题目。

class Solution:
    def subarraySum(self, nums: List[int], k: int) -> int:
        '''
        暴力法,超出时间限制
        res = 0
        for i in range(len(nums)):
            Sum = 0
            for j in range(i,len(nums)):
                Sum+=nums[j]
                if Sum==k:
                    res+=1
        return res
        '''
        #声明一个hash表,在python中其实就是字典的数据结构
        #默认键不存在的添加进去后value是1
        num_times = collections.defaultdict(int)
        #哈希表初始值是{0:1}
        num_times[0] = 1  
        #前缀和
        cur_sum = 0
        #结果计数器
        res = 0
        for i in range(len(nums)):
            cur_sum+=nums[i]
            #如果前缀和-k在hash表中,那就更新计数器呗
            if cur_sum-k in num_times:
                res+=num_times[cur_sum-k]
            #如果前缀和不在列表中的话就进行更新,已经存在的value+1;不存在的把key加入然后value设为1,因为coolection这个方法的特性,所以将两个和二为1了.
            num_times[cur_sum]+=1
        return res

此图来源leetcode官方解法:从左至右,从上到下;非常清晰的解释了hash+前缀和的方法 

 

总结:前缀和+哈希是解决数组问题中非常重要的一个方法,也是空间换时间的一个非常好的栗子。 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值