一、前置知识点:前缀和 + 哈希表
1. 前缀和(Prefix Sum)
- 定义:
prefix_sum[i]表示数组从第 0 个元素到第 i 个元素的累加和。 - 核心公式:子数组
[j+1, i]的和 =prefix_sum[i] - prefix_sum[j] - 作用:将 “计算区间和” 的操作从 O (n) 优化到 O (1),是解决子数组和问题的基础。
2. 哈希表统计频次
- 作用:快速记录并查询某个前缀和出现的次数,避免重复遍历。
- 关键思想:若
prefix_sum[i] - prefix_sum[j] = k,则prefix_sum[j] = prefix_sum[i] - k。遍历数组时,用哈希表保存已出现过的prefix_sum及其次数,即可在 O (1) 时间内找到满足条件的j的数量。
3. 初始化技巧
- 哈希表初始化为
{0: 1},表示 “前缀和为 0 时出现了 1 次”,用于处理从数组开头到当前位置的子数组和恰好为 k 的情况。
二、经典算法题:和为 K 的子数组(LeetCode 560)
题目描述
给定一个整数数组 nums 和一个整数 k,请你统计并返回该数组中和为 k 的子数组的个数。子数组是数组中元素的连续非空序列。
示例:
- 输入:
nums = [1, 1, 1, 1],k = 2 - 输出:
3 - 解释:和为 2 的子数组有
[1,1](索引 0-1)、[1,1](索引 1-2)、[1,1](索引 2-3),共 3 个。
最优解法:前缀和 + 哈希表
核心思路
- 遍历数组,维护当前前缀和
prefix_sum。 - 查询哈希表:若
prefix_sum - k存在于哈希表中,则说明之前有prefix_count[prefix_sum - k]个子数组的和为 k,将其累加到结果中。 - 更新哈希表:将当前前缀和
prefix_sum加入哈希表,记录其出现次数。 - 时间复杂度:O (n),仅遍历一次数组;空间复杂度:O (n),用于存储哈希表。
代码实现
from typing import List
class Solution:
def subarraySum(self, nums: List[int], k: int) -> int:
prefix_count = {0: 1} # 初始化:前缀和0出现1次
pre_sum = 0
result_count = 0
for num in nums:
pre_sum += num # 更新当前前缀和
target = pre_sum - k # 要找的历史前缀和
# 如果target存在,说明有对应子数组,累加次数
if target in prefix_count:
result_count += prefix_count[target]
# 更新当前前缀和的出现次数
prefix_count[pre_sum] = prefix_count.get(pre_sum, 0) + 1
return result_count
代码执行示例
以 nums = [1, 1, 1, 1],k = 2 为例:
- 初始化:
prefix_count = {0:1},pre_sum=0,result_count=0 num=1:pre_sum=1,target=1-2=-1(不存在)→prefix_count={0:1, 1:1}num=1:pre_sum=2,target=2-2=0(存在,次数 1)→result_count=1,prefix_count={0:1, 1:1, 2:1}num=1:pre_sum=3,target=3-2=1(存在,次数 1)→result_count=2,prefix_count={0:1, 1:1, 2:1, 3:1}num=1:pre_sum=4,target=4-2=2(存在,次数 1)→result_count=3,prefix_count={0:1, 1:1, 2:1, 3:1, 4:1}- 最终返回
3,与示例结果一致。
三、关键知识点总结
1. 暴力解法对比
- 暴力法:遍历所有子数组,计算和并判断是否为 k,时间复杂度 O (n²),在数据量大时会超时。
- 前缀和 + 哈希表:将时间复杂度优化到 O (n),是解决此类问题的标准高效解法。
2. 去重与计数逻辑
- 无需去重:题目允许多个相同的子数组,哈希表直接记录每个前缀和出现的次数即可。
- 核心计数:
result_count += prefix_count[target],表示有多少个历史前缀和能与当前前缀和组成和为 k 的子数组。
3. 适用场景拓展
前缀和 + 哈希表的组合,适用于以下类型题目:
- 和为目标值的子数组:如本题、LeetCode 523(连续的子数组和)
- 区间和问题:如统计和为 0 的子数组、和为奇数 / 偶数的子数组
- 连续子数组最值:如最长和为 k 的子数组
四、算法优化点回顾
| 优化点 | 作用 |
|---|---|
| 前缀和 | 将区间和计算从 O (n) 降为 O (1) |
| 哈希表 | 快速查询历史前缀和,避免二次遍历 |
| 初始化 {0:1} | 处理从数组开头到当前位置的子数组和为 k 的情况 |
| 一次遍历 | 整体时间复杂度降为 O (n),空间复杂度 O (n) |