文章目录
一、题目描述
题目链接:LeetCode - Subarray Sum Equals K
题意:
给定一个整数数组 nums 和一个整数 k,请你找出数组中 和等于 k 的子数组的个数。
示例:
输入:nums = [1,1,1], k = 2
输出:2
解释:子数组 [1,1] 有两个 ------ 第一个与第二个元素组成、第二个与第三个元素组成。
二、解题思路总览
Subarray Sum Equals K
暴力枚举
计算所有子数组的和
n^2
前缀和优化
用 prefix[i] 表示前 i 个元素的和
通过 prefix[j]-prefix[i] 判断子数组和
时间复杂度 O(n^2),空间 O(n)
哈希表优化
利用 Map 记录前缀和出现次数
prefixSum - k 能找到匹配数
时间复杂度 O(n),空间 O(n)
三、解法一:暴力枚举法
核心思想:
穷举所有可能的子数组 [i, j],计算它们的和,若等于 k,计入答案。
思路流程图
是
否
开始
初始化 count = 0
遍历 i 从 0 到 n-1
初始化 sum = 0
遍历 j 从 i 到 n-1
累加 sum += nums[j]
sum == k ?
计数 count++
继续 j++
输出 count
结束
Java 代码实现
java
public class SubarraySumEqualsK {
public int subarraySum(int[] nums, int k) {
int count = 0;
for (int i = 0; i < nums.length; i++) {
int sum = 0;
for (int j = i; j < nums.length; j++) {
sum += nums[j];
if (sum == k) {
count++;
}
}
}
return count;
}
}
复杂度分析
- 时间复杂度:O(n²)
- 空间复杂度:O(1)
四、解法二:前缀和优化版
核心思想:
创建一个前缀和数组 prefixSum[i] 表示从开头到第 i 个元素的累积和。
子数组 [i, j] 的和即为 prefixSum[j] - prefixSum[i-1]。
步骤示意图
nums: [1,2,3]
prefix[0]=0
prefix[1]=1
prefix[2]=3
prefix[3]=6
计算 prefix[j]-prefix[i]
Java 代码实现
java
public class SubarraySumEqualsK {
public int subarraySum(int[] nums, int k) {
int n = nums.length;
int[] prefix = new int[n + 1];
int count = 0;
for (int i = 1; i <= n; i++) {
prefix[i] = prefix[i - 1] + nums[i - 1];
}
for (int i = 0; i < n; i++) {
for (int j = i + 1; j <= n; j++) {
if (prefix[j] - prefix[i] == k) {
count++;
}
}
}
return count;
}
}
复杂度分析
- 时间复杂度:O(n²)
- 空间复杂度:O(n)
五、解法三:前缀和 + 哈希表优化版(最优解)
核心思想:
在遍历数组时,用一个哈希表记录"前缀和出现的次数"。
当我们计算出当前 prefixSum 后,如果存在 prefixSum - k 的记录,说明之前有一个前缀和使得当前这段子数组的和为 k。
思维导图
哈希表优化
当前前缀和 prefixSum
查找 prefixSum - k 是否存在
若存在,对应次数累加到结果
更新哈希表中 prefixSum 出现次数
流程演示
假设 nums = [1, 2, 3], k = 3
| 步骤 | 当前数字 | prefixSum | prefixSum-k | HashMap状态 | count结果 |
|---|---|---|---|---|---|
| 1 | 1 | 1 | -2 | {0:1,1:1} | 0 |
| 2 | 2 | 3 | 0 | {0:1,1:1,3:1} | 1(找到1次) |
| 3 | 3 | 6 | 3 | {0:1,1:1,3:1,6:1} | 2(再找到一次) |
Java 代码实现
java
import java.util.HashMap;
import java.util.Map;
public class SubarraySumEqualsK {
public int subarraySum(int[] nums, int k) {
Map<Integer, Integer> prefixCount = new HashMap<>();
prefixCount.put(0, 1); // 初始前缀和为 0
int prefixSum = 0;
int count = 0;
for (int num : nums) {
prefixSum += num;
if (prefixCount.containsKey(prefixSum - k)) {
count += prefixCount.get(prefixSum - k);
}
prefixCount.put(prefixSum, prefixCount.getOrDefault(prefixSum, 0) + 1);
}
return count;
}
}
复杂度分析
- 时间复杂度:O(n)
- 空间复杂度:O(n)
总结
| 解法类型 | 时间复杂度 | 空间复杂度 | 适用场景 |
|---|---|---|---|
| 暴力枚举 | O(n²) | O(1) | 数据规模较小 |
| 前缀和优化 | O(n²) | O(n) | 理解前缀和概念 |
| 哈希表优化 | O(n) | O(n) | 最优解法,适合大规模数据 |