LeetCode笔记-用前缀和与哈希表解决【560-和为k的子数组】
2020年10月22日
给定一个整数数组和一个整数 k,你需要找到该数组中和为 k 的连续的子数组的个数。
- 数组的长度为 [1, 20,000]
- 数组中元素的范围是 [-1000, 1000] ,且整数 k 的范围是 [-1e7, 1e7]
示例
1 | 输入:nums = [1,1,1], k = 2 |
思路
看到这种子集合,我一开始想到了动态规划,题解如下:
我们做一张表对应数组[a,b,c]
:
a | b | c | |
---|---|---|---|
a | a | 0 | 0 |
b | a+b | b | 0 |
c | a+b+c | b+c | c |
列为起始元素,行为结束元素
可以推出我们每行的值等于上一行的值加上本行对应元素。每行只填到自身。对应代码如下:
1 | def subarraySum(self, nums: List[int], k: int) -> int: |
时间复杂度为O(N^2)
感觉并没有发挥DP的优势,跟暴力穷举的时间复杂度类似了。提交后果然超时。
观看LeetCode相关讨论后学习到本题需要使用前缀和与哈希表解决:
题解
使用前缀和
设我们有一个数组A[]
,其中对应n
位置的前缀和为A[0]
累加至A[n-1]
,依此我们可以构建前缀和数组prev_sums[]
,当我们需要知道从A[i]
到A[j]
的子数组和时,我们只需计算prev_sums[j+1]-prev_sums[i]
,对应的,我们修改代码至如下:
1 | def subarraySum(self, nums: List[int], k: int) -> int: |
使用哈希表(字典)
这时,时间复杂度依然为O(N^2)
,为了进一步优化,需要想到,我们只需计算和为k的子数组数量,也就是prev_sums[i+1]-prev_sums[j]=k
的数量,就能得出最后的答案。我们可以利用字典中查找key
复杂度只为O(1)
的特性,把每个prev_sum
存储到一个字典的key
中,同时把该prev_sum
的个数存储到值中。同时在循环中我们只考虑j<=i
的情况,所以我们只需要维护一个存储key为prev_sum
,值为prev_sum的个数
的字典,遍历一次,在遍历过程中,计算出当前prev_sum-k
的值,并在字典中查找这个key,如果有对应的值则把值加到count
上,最后得出答案。
最终代码
1 | def subarraySum(self, nums: List[int], k: int) -> int: |
此算法时间复杂度为O(N)
。