2025.8.18
560. 和为 K 的子数组串
题目
给你一个整数数组 nums 和一个整数 k ,请你统计并返回 该数组中和为 k 的子数组的个数 。
子数组是数组中元素的连续非空序列。
示例 1:
输入:nums = [1,1,1], k = 2
输出:2
示例 2:
输入:nums = [1,2,3], k = 3
输出:2
提示:
1 <= nums.length <= 2 * 104
-1000 <= nums[i] <= 1000
-107 <= k <= 107
总体思路
暴力求解
- 枚举所有子数组:
外层循环固定子数组的起点 i。
内层循环枚举子数组的终点 j。 - 逐步累加:
从 i 到 j 的元素累加求和,看是否等于 k。 - 计数:
如果和等于 k,就让 count++。 - 时间复杂度:O(n²)
前缀和 + 哈希表
- 前缀和定义:
pre[i] = nums[0] + nums[1] + … + nums[i]。
任意子数组和 nums[l…r] = pre[r] - pre[l-1]。
所以我们只要知道某个前缀和 pre[r]-k 之前出现过几次,就能知道有多少个子数组以 r 为结尾,和等于 k。 - 哈希表 m:
- key:某个前缀和的值
- value:这个前缀和出现的次数
- 初始化:
m[0] = 1,表示“前缀和 0 出现过 1 次”,这样当 pre == k 时,能正确计数从 0 开始的子数组。 - 遍历数组:
- 更新当前前缀和:pre += nums[i]
- 在哈希表里查找:pre-k 出现过多少次,把次数加到答案上。
- 把当前前缀和 pre 存入哈希表(出现次数 +1)。
- 时间复杂度:O(n)
代码
golang
//暴力求解
func subarraySum(nums []int, k int) int {
n := len(nums)
count := 0
for i:=0; i<n; i++ {
sum := nums[i]
if sum == k {
count++
}
for j:=i+1; j<n; j++ {
sum += nums[j]
if sum == k {
count++
}
}
}
return count
}
// 前缀和+哈希表
// 统计和为 k 的子数组个数:前缀和 + 哈希表
func subarraySum(nums []int, k int) int {
n := len(nums)
count, pre := 0, 0
// 方式一:字面量初始化并预置 m[0]=1,表示前缀和为 0 出现过 1 次
// m 的含义:m[x] 表示“前缀和等于 x 的出现次数”
m := map[int]int{0: 1}
for i := 0; i < n; i++ {
pre += nums[i] // 维护当前前缀和
// 需要的“目标前缀和”是 pre-k,出现几次就能形成几个子数组
count += m[pre-k] // 若不存在,m[pre-k] 默认是 0(见下文语法点)
/*if _, ok := m[pre - k]; ok {
count += m[pre - k]
}*/ //不必要写这一步
m[pre] = m[pre] + 1 // 记录当前前缀和出现次数 +1
}
return count
}
// 前缀和+哈希表,无注释版本
func subarraySum(nums []int, k int) int {
n:=len(nums)
count:=0
pre:=0
m := map[int]int{0:1}
for i:=0;i<n;i++ {
pre+=nums[i]
count+=m[pre-k]
m[pre]+=1
}
return count
}
golang知识
-
map 默认值特性
count += m[pre-k] 这一句很妙。
在其他语言(比如 Java、C++)里,访问不存在的 key 会报错或需要额外判断。
在 Go 里,访问不存在的 key 会返回“零值”,这里是 0。
所以可以少写 if 判断,代码更简洁。 -
预置 m[0] = 1
这是算法的小技巧。
它让“从下标 0 开始的子数组”也能被统计到,否则会漏掉。