和为K的子数组

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

示例 1 :

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


暴力法还是比较好想的, 按照题目要求, 把每种情况都列出来, 然后统计出结果就行了. 总共需要O(n^2)的时间复杂度, 自然是结果正确, 但是超时.

class Solution {
    func subarraySum(_ nums: [Int], _ k: Int) -> Int {
        // 统计数量
        var result = 0
        let n = nums.count

        for i in 0..<n {
            // 计算i到j之间的和
            var sum = 0
            for j in i..<n {

                sum += nums[j]
                // 满足条件,数量+1
                if sum == k {
                    result += 1
                }

            }

        }
        return result
    }

}

暴力法不行, 然后就想到了滑动窗口来解决, 但是但是, 发现这个题目里可以有负数的, 滑动窗口要求数组里的都是正数, 这种方法也不行.

好吧, 忍不住看了题解, 题解采用了 前缀和 + 哈希表优化 , 对于数组, 我们计算好好一个新的数组, 新的数组中是前i项的和,

比如: 原始数组是: [5 , -1 , 1, 2, -2]

  计算的前缀和是:[5, 4, 5 , 7 , 5], 

对于第i项, 如果在sum中存在一个值sum[j]正好等于sum[i] - k, 那么就可以计数一次,

还是来一个例子, 比如k = 3; 正好存在sum[1] = sum[3] -k , 那就说明从下标为1开始的数字到下标为3的数字组成的数组满足了条件, 注意是(1,3], 左开右闭 , 就是 [1,2]; 

如果k=2, 会出现2个,  [2], [-1 , 1, 2,],  需要统计出sum[j] == 5的个数, 所以需要考虑用一个字典了, key是前缀和, value是此前缀和的出现次数.

还有一种情况, 如果k==5, 有3个[5], [5 , -1 ,1],  [5 , -1 , 1, 2, -2], 但是sum[i] - k却是一个都没有, 需要添加一个dic[0] = 1的值, 这样在计算sum[i] == k的时候不会遗漏.

上面的原始思路, 在做的过程中, 发现一些可以优化的点,

首先用来存储前缀和的数组不是必要的, 可以用一个sumI+hash表来优化 ,   用一个hash表来存储前缀和出现的次数, 可以快速的知道sum[j]的出现次数. 当然, 经过优化后, 理解难度上也大了一点. 时间复杂度为O(n), 只需要一次遍历即可

class Solution {

    func subarraySum(_ nums: [Int], _ k: Int) -> Int {

        // 计算前项和
        var sumI = 0
        // 统计和为sum的个数
        var dic = [Int:Int]()
        // 记录一个初始值,
        dic[0] = 1
        var result = 0
        let n = nums.count
        for i in 0..<n {
            let item = nums[i]
            // 计算前I项的和
            sumI += item
            // 更新结果,如果字典里没有就result+0
            result += dic[sumI-k] ?? 0
            // 把前项和出现的次数更新到字典里
            dic[sumI, default : 0] += 1
        }
        return result
    }

}

 

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值