主题切换
LeetCode 补拙笔记
0. 前言
- 日期:2026.06.28
- 题目:560. 和为 K 的子数组
- 难度:中等
- 标签:前缀和、哈希表、数组
1. 题目理解
问题描述 给定整数数组 nums 和整数 k,统计数组中连续非空子数组元素之和等于 k 的子数组总数量。
示例
输入:nums = [1,1,1], k = 2 输出:2 解释:子数组 [0,1]、[1,2] 总和都等于 2,共2个。
2. 解题思路
核心观察
- 定义前缀和
pre[i]:nums[0] ~ nums[i]全部元素累加和; - 区间
[j+1, i]的和 =pre[i] - pre[j],要求该值等于k,等价于寻找pre[j] = pre[i] - k; - 使用哈希表存储此前所有前缀和出现的次数,遍历到
pre[i]时直接累加符合条件的pre[j]数量; - 初始哈希表存入
{0:1},用来处理pre[i] == k的边界情况,消除单独判断分支。
算法步骤
- 初始化哈希表,存入前缀和0,出现次数1;
- 遍历数组,实时维护当前前缀和;
- 计算目标值
curSum - k,若哈希表存在该值则累加对应次数; - 将当前前缀和更新存入哈希表;
- 遍历结束返回总计数。
3. 代码实现
java
package lc560;
import java.util.HashMap;
class Solution {
public int subarraySum(int[] nums, int k) {
int res = 0;
int n = nums.length;
int[] pre = new int[n];
HashMap<Integer, Integer> map = new HashMap<>();
pre[0] = nums[0];
for (int i = 1; i < n; i++) {
pre[i] = nums[i] + pre[i - 1];
}
for (int i = 0; i < n; i++) {
if (pre[i] == k) {
res++;
}
int need = pre[i] - k;
if (map.containsKey(need)) {
res += map.get(need);
}
map.put(pre[i], map.getOrDefault(pre[i], 0) + 1);
}
return res;
}
}4. 代码优化说明
java
import java.util.HashMap;
class Solution {
public int subarraySum(int[] nums, int k) {
HashMap<Integer, Integer> map = new HashMap<>();
// 初始前缀和0出现1次,直接覆盖pre[i]==k场景,消除if判断
map.put(0, 1);
int curSum = 0, ans = 0;
for (int num : nums) {
curSum += num;
int target = curSum - k;
// getOrDefault替代containsKey分支判断,不存在时直接取0
ans += map.getOrDefault(target, 0);
map.put(curSum, map.getOrDefault(curSum, 0) + 1);
}
return ans;
}
}5. 复杂度分析
- 原版前缀和数组版本 时间复杂度:
,两次线性遍历数组,存在两处if条件分支 空间复杂度: ,额外开辟前缀和数组与哈希表 - 优化版一维遍历 时间复杂度:
,仅单次遍历数组,删除多余条件分支 空间复杂度: ,取消前缀和数组,仅保留哈希表存储前缀和频次
6. 总结
- 核心:前缀和公式转换
pre[i] - pre[j] = k,用哈希表统计历史前缀和频次; - 优化亮点:
- 初始化
map.put(0,1)消除pre[i]==k的独立if判断; - 实时累加
curSum,取消存储全部前缀和的数组,节省空间; getOrDefault替代containsKey多层分支,代码更简洁;
- 初始化
- 关键:子数组连续的特性是前缀和算法成立的前提。