给定一个整数数组和一个整数 k,您需要找到该数组中总和为 k 的连续子数组的数量。
示例 1:
输入:nums = [1,1,1], k = 2
输出:2、1,1] 和 [1,1] 是两种不同的情况。
注意:数组的长度为 [1, 20,000]。
数组中元素的范围是 [-1000, 1000],整数 k 的范围是 [-1e7, 1e7]。
哈希表前缀和阿里腾讯字节的直观方法是暴力破解所有子数组,然后分别计算总和,如果等于 k,则计数为 +1这种做法的时间复杂度为 o(n 2),* 如下所示:
class solution: def subarraysum(self, nums: list[int], k: int) -int: cnt, n = 0, len(nums) for i in range(n): sum = 0 for j in range(i, n): sum += nums[j] if (sum == k): cnt += 1 return cnt
事实上,当我第一次看到这个问题时,我想,“有没有可能用推拉窗来解决它? ”。但我很快就放弃了,因为数组中的项范围为负值,这使得我们扩展或收缩窗口变得更加复杂。 第二个思路是给 sum 加上前缀,保存一个数组的前缀 sum,然后用 difference 方法推导任意区间的和,这是可行的,如下所示:
class solution: def subarraysum(self, nums: list[int], k: int) -int: cnt, n = 0, len(nums) pre = [0] *n + 1) for i in range(1, n + 1): pre[i] = pre[i - 1] +nums[i - 1] for i in range(1, n + 1): for j in range(i, n + 1): if (pre[j] -pre[i - 1] == k): cnt += 1 return cnt
但是,问题只是要求它总而不必找到所有的子数组。 因此,我们可以直接使用以下时间复杂度为 $o(n)$ 的算法。
这种方法的核心点是,找到的子阵列总数实际上等于:
以索引 0 结尾的子数组数以索引 1 结尾的子数组数... 以索引 n - 1 结尾的子数组数和以索引 i 结尾的子数组数等于:前缀为 sum 的子数组数为 acc - k,其中 acc 是当前前缀的总和。 为了能够快速提取前缀和 acc - k 的数量,我们可以将其存储在哈希中。
具体算法:维护一个算力图,算力图的键是累积值 acc,该值是累积值 acc 的出现次数。 遍历数组,然后不断更新 acc 和哈希图,如果 acc 等于 k,那么很明显 +1如果hashmap[acc - k]存在,我们可以将其添加到结果中。 语言很难解释,所以我画了一个图表来说明 nums = [1,2,3,3,0,3,4,2], k = 6 的情况。
如图所示,当访问 nums[3] 时,hashmap 如图所示,计数为 2其中之一是[1,2,3],这很容易理解。 另一个是[3,3]。
这个 [3,3] 正是我们用 hashmap[acc - k] 得到的,即 hashmap[9 - 6]。
前缀,并可用于通过利用哈希映射记录的总和来避免重复计数语言支持: js, pythonj**ascript code:
/* *lc app=leetcode id=560 lang=j**ascript * 560] subarray sum equals k *//** param nums * param k * return */var subarraysum = function (nums, k) ;let acc = 0; let count = 0; for (let i = 0; i < nums.length; i++)if (hashmap[acc] === void 0) else }return count;};
python code:
class solution: def subarraysum(self, nums: list[int], k: int) -int: d = {}acc = count = 0 for num in nums: acc += num if acc == k: count += 1 if acc - k in d: count += d[acc-k] if acc in d: d[acc] += 1 else: d[acc] = 1 return count