LeetCode10:和为K的数组
10.和为K的数组
题目
给你一个整数数组 nums
和一个整数 k
,请你统计并返回 该数组中和为 k
的子数组的个数 。
子数组是数组中元素的连续非空序列。
示例 1:
1 |
|
示例 2:
1 |
|
提示:
1 <= nums.length <= 2 * 104
-1000 <= nums[i] <= 1000
-107 <= k <= 107
题解
子数组是数组中连续的一段元素。
例如,对于数组
nums = [a, b, c, d]
,以下都是子数组:[a]
[a, b]
[b, c, d]
[c]
注意:子数组要求连续,因此
[a, c]
不是子数组,因为中间缺了b
。
一、暴力枚举
由于子数组的长度不确定,无法确定是由几个数字求得的K,采用暴力枚举,尝试所有可能。需要确定起点和终点:
外层循环确定子数组的起点:假设数组的长度为 n,我们可以让起点 i
从 0 到 n-1 遍历。
内层循环确定子数组的结束点:
对于固定起点 i
,结束点 j
从 i
开始遍历到 n-1。这样可以确保子数组是连续的,即子数组为 nums[i]
到 nums[j]
(包含两端)。
计算子数组的和:
对于每个确定的起点 i
和结束点 j
,计算子数组 [nums[i], nums[i+1], ..., nums[j]]
的和,并检查是否等于 k。如果等于 k,则计数器加 1。
1 |
|
二、前缀和+哈希表(优化)
子数组的和可以通过两个前缀和的差来计算。这是一种常见的算法技巧:如果我们维护每个索引的前缀和,那么对于每个索引,可以通过哈希表快速查找之前存在的前缀和(即当前前缀和减去k)。这种方法将时间复杂度从O(n^2)降低到O(n)。
思路概述
前缀和
定义prefix[i]
为从数组开始到第 i 个元素(包含 i)的和。对于任意子数组
nums[i..j]
,其和为:sum(i,j)=prefix[j]-prefix[i-1]
。特别地,令
prefix[-1] = 0
。转换问题
若要求prefix[j] - prefix[i-1] = k
,则可以转换为:prefix(i-1)=prefix[j]-k
。也就是说,当遍历到第
j
个元素时,如果之前存在某个前缀和等于prefix[j] - k
,则从该位置之后到j
的子数组之和为k
。使用哈希表记录前缀和出现次数
- 初始化哈希表(例如 Python 中的字典),令
prefix_count[0] = 1
,表示空前缀和 0 出现一次。 - 遍历数组,累计前缀和
prefix_sum
,并检查prefix_sum - k
是否在哈希表中出现过,若出现过,则将对应的次数累加到结果中。 - 最后更新哈希表中当前前缀和
prefix_sum
的出现次数。
- 初始化哈希表(例如 Python 中的字典),令
这样可以将时间复杂度降低到 O(n)。
1 |
|
知识点
子数组是否考虑去重?
由题目给的示例,不需要。只要和为k都计入。
1
2输入:nums = [1,1,1], k = 2
输出:2prefix_count字典的作用?
用于记录遍历数组过程中,每个前缀和出现的次数。
因为在遍历到索引
j
时,计算当前的前缀和prefix_sum
,再从字典查找是否存在prefix_sum - k
,字典中存储了该前缀和出现的次数,就可以直接累加到最终的计数中。初始化{0:1}的作用
相当于一个虚拟的起点。因为在开始累加前,前缀和必定是 0。只有在累加过程中,当前缀和等于 k 时,通过
prefix_sum - k = 0
查找,才能正确计数那些从数组起始开始的满足条件的子数组。