题目:
给你一个整数数组nums和一个整数k,请你统计并返回该数组中和为k的子数组的个数
子数组是数组中元素的连续非空序列
方法一:暴力枚举法
枚举nums的所有子数组,统计其中和为k的子数组个数。
考虑以i结尾和为k的连续子数组的个数,统计符合条件的下标j的个数,其中0<=j<=i,且这个子数组的和为k。可以枚举[0,,,i]里所有的下标j来判断是否符合条件。
class Solution(object):
def subarraySum(self, nums, k):
"""
:type nums: List[int]
:type k: int
:rtype: int
"""
count=0 #记录和为k的子数组的个数,初始值为0
for start in range(len(nums)): #遍历数组中的每一个元素作为子数组的起始位置
sum=0 #表示从start到当前end索引位置的子数组的和
for end in range(start,-1,-1): #遍历从start到0的所有索引
sum+=nums[end]
if sum==k: #检查子数组的是否等于目标值k
count+=1
return count #返回和为k的子数组的数目
时间复杂度:O(n2)其中 n 为数组的长度。枚举子数组开头和结尾需要 O(n2) 的时间,其中求和需要 O(1) 的时间复杂度,因此总时间复杂度为 O(n2)。
空间复杂度:O(1)只需要常数空间存放若干变量
测试可以通过,但是提交会超过时间限制
方法二:前缀和+哈希表优化
可以使用前缀和和数组进一步优化方法一。定义前缀和数组pre使得pre[i]为nums[0],nums[i]中所有数的和,则pre[i]可以从pre[i-1]得到,即pre[i]=pre[i-1]+nums[i]
那么[j,,i]这个子数组的和为k,这个条件可以转换为pre[i]-pre[j-1]=k.简单移项可得到符合条件的下标j,需要满足pre[j-1]==pre[i]-k。所以可以考虑以i结尾的和为k的连续子数组个数时只要统计有多少个前缀和为pre[i]-k的pre[j]即可。
建立哈希表mp,以前缀和pre[i]为键,出现的次数为对应的值,记录pre[i]出现的次数,从左到右边更新mp边计算答案,那么以i结尾的答案mp[pre[i]-k]即可在O(1)时间内得到,最后的答案即为所有下标结尾和为k的子数组个数之和。
class Solution(object):
def subarraySum(self, nums, k):
"""
:type nums: List[int]
:type k: int
:rtype: int
"""
count=0 #初始化和为k的数组的数量
sum=0 #初始化当前子数组的和
map={0:1} #字典,记录前缀和出现的次数,考虑了空数组的情况
for num in nums:
sum +=num #对于每个元素,更新当前子数组的和
if sum-k in map: #sum-k存在,意味着之前有一个子数组的和为sum -k,而当前的和为 sum,因此这两个子数组的和加起来就是 k
count +=map[sum-k]#如果sum-k在map中出现过,表示从某些位置到当前元素子数组和为k
map[sum]=map.get(sum,0)+1 #更新字典 map 中当前和 sum 的计数
#map.get(sum,0)的作用是,如果字典中没有键sum,则返回默认值 0,否则返回当前sum的计数然后将计数加 1
return count
时间复杂度:O(n)其中 n 为数组的长度。我们遍历数组的时间复杂度为 O(n),中间利用哈希表查询删除的复杂度均为 O(1),因此总时间复杂度为 O(n)
空间复杂度:O(n)其中 n 为数组的长度。哈希表在最坏情况下可能有 n 个不同的键值,因此需要 O(n) 的空间复杂度