1. 题目解析与核心思路
560题要求统计数组中连续子数组的和等于给定值k的出现次数。这道题看似简单,但直接暴力枚举所有子数组的O(n²)解法在LeetCode上会超时。我们需要更聪明的解法。
1.1 暴力解法的问题
最直观的解法是双重循环遍历所有子数组:
count = 0 for i in range(len(nums)): sum = 0 for j in range(i, len(nums)): sum += nums[j] if sum == k: count += 1 return count这种解法时间复杂度O(n²),当n=20000时,运算量会达到4亿次,显然无法通过测试。
1.2 前缀和优化思路
前缀和(preSum)是一种常见的数组处理技巧。我们定义preSum[i]表示nums[0..i-1]的和,那么子数组nums[i..j]的和可以表示为preSum[j+1]-preSum[i]。
通过预处理得到前缀和数组后,问题转化为:找到所有i<j使得preSum[j]-preSum[i]=k。这仍然需要O(n²)时间,没有本质改进。
2. 哈希表优化解法
2.1 核心思路突破
关键突破点是:在计算前缀和的过程中,用哈希表记录每个前缀和出现的次数。这样可以在O(1)时间内查询到需要的值。
具体步骤:
- 初始化哈希表prefix_sum_count,记录前缀和出现的次数,初始时prefix_sum_count[0] = 1
- 遍历数组,计算当前前缀和curr_sum
- 查找哈希表中curr_sum - k出现的次数,累加到结果
- 将当前curr_sum存入哈希表
2.2 代码实现
def subarraySum(nums, k): from collections import defaultdict prefix_sum_count = defaultdict(int) prefix_sum_count[0] = 1 curr_sum = 0 count = 0 for num in nums: curr_sum += num if curr_sum - k in prefix_sum_count: count += prefix_sum_count[curr_sum - k] prefix_sum_count[curr_sum] += 1 return count2.3 时间复杂度分析
该算法只需要一次遍历数组,每次哈希表的插入和查询操作都是O(1),因此总时间复杂度为O(n),空间复杂度O(n)(哈希表存储空间)。
3. 边界条件与注意事项
3.1 特殊测试用例
- 空数组:应返回0
- 全零数组:[0,0,0], k=0,应返回6(所有子数组都满足)
- 包含负数的数组:[-1,-1,1], k=0,应返回1
- 单个元素等于k:[1], k=1,应返回1
3.2 实现细节
- 必须初始化prefix_sum_count[0]=1,考虑从数组开头开始的子数组
- 要先查询curr_sum - k,再更新哈希表,避免重复计数
- 使用defaultdict可以简化代码,避免键不存在的判断
4. 同类问题扩展
4.1 类似题目推荐
- 和等于k的最长子数组长度
- 连续的子数组和
- 连续数组(0和1数量相等)
- 和相同的二元子数组
4.2 算法模式总结
这类"子数组和"问题通常可以转化为前缀和问题,配合哈希表实现高效查询。关键在于:
- 将子数组和表示为前缀和的差值
- 用哈希表记录前缀和的出现情况
- 在遍历过程中实时查询和更新
5. 实际应用场景
这种算法在实际开发中有广泛应用:
- 金融分析:统计某段时间内交易金额累计达到特定值的区间
- 日志分析:查找访问量达到阈值的连续时间段
- 信号处理:检测信号强度累计达到特定值的时段
- 用户行为分析:识别用户连续活跃达到特定天数的区间
6. 性能优化技巧
- 对于特定场景(如元素范围有限),可以用数组代替哈希表
- 如果只需要判断是否存在而非计数,可以用集合代替字典
- 在内存受限时,可以边计算边处理,不存储整个前缀和数组
7. 常见错误与调试
- 忘记初始化prefix_sum_count[0]=1
- 更新哈希表和查询的顺序错误
- 没有考虑整数溢出问题(Python不需要,但其他语言要注意)
- 对负数处理不当,特别是k为负数的情况
提示:在面试中,建议先提出暴力解法,再逐步优化到哈希表解法,展示思考过程。