子数组和问题深度解析:前缀和+哈希表的经典应用

一、问题定义

给定整数数组 nums 和目标值 k,计算有多少个连续子数组的和恰好等于 k

示例:
输入: nums = [1, 2, 3], k = 3
输出: 2
解释: [1,2] 和 [3] 两个子数组的和为3

二、常见误区:为什么不能用滑动窗口?

⚠️ 关键陷阱:滑动窗口仅适用于非负数组。当数组包含负数或零时,窗口无法单调移动,会导致错误结果。

例如 nums = [1, -1, 1, 1], k = 2,滑动窗口会漏掉 [1, -1, 1, 1] 这个有效子数组。

三、正确解法:前缀和 + 哈希表

核心思路

  1. 前缀和定义prefixSum[i] = 从索引0到i的元素累加和
  2. 关键观察:若存在 prefixSum[j] - prefixSum[i] = k,则子数组 [i+1...j] 的和为k
  3. 哈希表优化:边遍历边记录每个前缀和出现的次数,实现O(1)查询

算法步骤

def subarraySum(nums, k):
    from collections import defaultdict
    
    # 哈希表:前缀和 -> 出现次数
    prefix_count = defaultdict(int)
    prefix_count[0] = 1  # 初始化:空前缀和为0
    
    current_sum = 0
    result = 0
    
    for num in nums:
        current_sum += num
        
        # 查找是否存在 prefix_sum = current_sum - k
        if (current_sum - k) in prefix_count:
            result += prefix_count[current_sum - k]
        
        # 记录当前前缀和
        prefix_count[current_sum] += 1
    
    return result
💡 为什么初始化 prefix_count[0] = 1?
表示"从数组开头到当前位置的和恰好为k"的情况。例如 nums = [3], k = 3,需要这个初始值才能正确计数。

四、复杂度分析

五、面试追问与应对

Q1: 能否优化空间到O(1)?

A: 对于"统计数量"的问题,无法优化。但如果只需判断"是否存在",可以用集合代替哈希表。

Q2: 负数会影响算法吗?

A: 不影响前缀和方案,但会让滑动窗口完全失效。这正是前缀和方案的优势所在。

Q3: 为什么要记录前缀和的"出现次数"?

A: 因为多个不同的子数组可能有相同的前缀和差值。例如 [1, -1, 1], k=1,需要统计所有符合条件的组合。

六、扩展变体

前缀和 哈希表 子数组 算法面试 面试辅助 VO辅助 VO代面 OA辅助