力扣(LeetCode)100题:560.和为K的子数组

560.和为K的子数组

我的题解:暴力,因为我实在想不到其他解法了┭┮﹏┭┮还超时了这里就不放出来了。太菜了

官方题解:前缀和+哈希表

class Solution:
    def subarraySum(self, nums: List[int], k: int) -> int:
        s = [0] * (len(nums) + 1)
        for i, x in enumerate(nums):
            s[i + 1] = s[i] + x

        cnt = defaultdict(int)
        ans = 0
        for sj in s:
            ans += cnt[sj - k]
            cnt[sj] += 1
        return ans

这段代码是解决「和为 K 的子数组」(LeetCode 560)问题的经典解法,使用了前缀和(Prefix Sum) + 哈希表优化的思想。下面按照你要求的格式,先解释关键函数和表达式的含义,再逐段解析整体逻辑。


🔹 关键函数与表达式说明

1. s = [0] * (len(nums) + 1)
  • 创建一个长度为 n + 1 的前缀和数组 s,其中 s[0] = 0
  • 定义:s[i] = nums[0] + nums[1] + ... + nums[i-1],即前 i 个元素的和。
  • 这样设计的好处是:任意子数组 nums[i:j](左闭右开)的和可表示为 s[j] - s[i]
2. defaultdict(int)
  • 来自 collections 模块,是一个默认值为 0 的字典。
  • 当访问一个不存在的键时,不会报错,而是自动创建该键并赋值为 0
  • 用于高效统计某个前缀和出现的次数。
3. ans += cnt[sj - k]
  • 核心思想:我们想找有多少对 (i, j) 满足 s[j] - s[i] = k,即 s[i] = s[j] - k
  • 对于当前前缀和 sj = s[j],只要知道之前有多少个前缀和等于 sj - k,就能得到以 j 结尾、和为 k 的子数组个数。
  • cnt[sj - k] 正是这个数量。
4. cnt[sj] += 1
  • 将当前前缀和 sj 记录到哈希表中,供后续位置使用。
  • 注意:先查询 sj - k,再更新 cnt[sj],避免把当前前缀和自己也算进去(因为子数组长度至少为 1,i < j)。

🔸 整体逻辑解析

步骤 1:构建前缀和数组
s = [0] * (len(nums) + 1)
for i, x in enumerate(nums):
    s[i + 1] = s[i] + x

例如:nums = [1, 2, 3]s = [0, 1, 3, 6]

  • s[0] = 0
  • s[1] = 1
  • s[2] = 1+2 = 3
  • s[3] = 1+2+3 = 6

子数组 nums[1:3] = [2,3] 的和 = s[3] - s[1] = 6 - 1 = 5


步骤 2:遍历前缀和,用哈希表统计满足条件的对数
cnt = defaultdict(int)
ans = 0
for sj in s:
    ans += cnt[sj - k]
    cnt[sj] += 1
  • 初始化 cnt 为空,但隐含 cnt[0] = 0(尚未记录)。
  • 关键点:在循环开始时,cnt 中只包含当前 sj 之前的前缀和
🌰 举例说明

nums = [1, 1, 1], k = 2

前缀和 s = [0, 1, 2, 3]

遍历过程:

sjsj - kcnt[sj - k]ans 累加后然后执行 cnt[sj] += 1
0-200cnt[0] = 1
1-100cnt[1] = 1
2011cnt[2] = 1
3112cnt[3] = 1

最终 ans = 2,对应子数组 [1,1](索引 01)和 [1,1](索引 12),正确。

💡 为什么一开始 cnt[0] 要被计入?
因为当 sj == k 时,sj - k = 0,而 s[0] = 0 表示“从开头到当前位置”的子数组。所以必须让 cnt[0] 在适当时候为 1 —— 这正是通过先查询、后更新,并且从 s[0]=0 开始遍历实现的。


📊 复杂度分析

  • 时间复杂度:O(n)
    只需一次遍历计算前缀和,再一次遍历统计答案,每次哈希操作 O(1)。

  • 空间复杂度:O(n)
    前缀和数组 s 占 O(n),哈希表 cnt 最多存 n+1 个不同前缀和。

⚡ 优化提示:其实可以不用显式存储整个 s 数组,边遍历 nums 边维护当前前缀和,将空间优化到 O(1)(除哈希表外)。但当前写法逻辑更清晰。


总结

这段代码的核心思想是:

子数组和 = 两个前缀和之差
要找 sum(i, j) = k,等价于找 s[j] - s[i] = k,即 s[i] = s[j] - k
用哈希表记录所有已出现的 s[i] 的频次,就能在 O(1) 时间内知道有多少个 i 满足条件。

通过前缀和 + 哈希表计数,将暴力 O(n²) 的解法优化到 O(n),是处理“子数组和”类问题的标准范式。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值