和为k的子数组(map统计前缀和)
题目链接:https://leetcode-cn.com/problems/subarray-sum-equals-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
题目分析:
求和为k的子数组数量
我们从暴力解法一步步推导
1.首先最基础的暴力是三层循环,两层分别确定区间起始点,最后一层计算此区间和是否为k,时间复杂度为O(N * N * N)
2.改进一下,简化最后一层循环,用前缀和来计算每次的区间和,时间复杂度:O(N*N)
3.我们起始不需要知道满足要求的子数组到底是哪些,只要知道到底有多少个符合要求的子数组即可
- 我们知道每个元素都对应一个前缀和preSum,在遍历数组得到某个元素i的前缀和preSum[i]的时候,如果i之前存在一个历史前缀和,使得当前前缀和减去历史前缀和等于k(即preSum[i]-preSum[j-1]==k),那么此子数组符合要求,计数+1
- 所以我们只需要在遍历数组时,利用一个map记录下每个元素的前缀和,再每次统计看有没有符合要求的历史前缀和即可
- 时间复杂度:O(N),空间复杂度:O(N)
func subarraySum(nums []int, k int) int {
preSum:=0 // 前缀和
mPreSum:=make(map[int]int) // 统计某一个值的前缀和出现次数
mPreSum[0]=1 // 前缀和为0的子数组 出现了一次 ,让下标-1位置对应的前缀和为0
count:=0 // 计数器,每次统计符合要求的历史前缀和,即符合要求的子数组数量
for i:=0;i<len(nums);i++{
preSum+=nums[i] // 对每个元素,更新前缀和
// 判断此时有没有符合要求的历史前缀和,有则统计
if _,ok:=mPreSum[preSum-k];ok{
count+=mPreSum[preSum-k]
}
// 值为preSum的前缀和出现次数+1
mPreSum[preSum]++
}
return count
}