题目描述
给你一个整数数组 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
解法一
思路:
枚举算法,从下标i
开始,统计后面所有位置的字串和,当中若出现和为k
则个数加一
代码:
public class leetcode_010 {public static int subarraySum(int[] nums, int k) {int ans=0;int tempSum=0;for(int i=0;i<nums.length;i++){tempSum+=nums[i];if(tempSum==k)ans++;for (int j=i+1;j<nums.length;j++){tempSum+=nums[j];if(tempSum==k)ans++;}tempSum=0;}return ans;}public static void main(String[] args) {Scanner sc = new Scanner(System.in);String[] line = sc.nextLine().split(",");int[] nums= Arrays.stream(line).mapToInt(Integer::parseInt).toArray();int k = sc.nextInt();int res = subarraySum(nums,k);System.out.println(res);}
}
解法二
思路:
来自官方的解答,采用前缀和和哈希表进行优化,时间复杂度为O(n)。
- 前缀和(Prefix Sum):定义
pre[i]
为数组从索引0
到i
的所有元素之和。例如,对于数组nums
,pre[i] = nums[0] + nums[1] + ... + nums[i]
。 - 子数组和:子数组
[j..i]
的和可以表示为pre[i] - pre[j-1]
。注意,当j=0
时,pre[j-1]
定义为0
(即没有元素时的前缀和)。 - 目标条件:我们想找到所有满足
pre[i] - pre[j-1] == k
的子数组。这等价于pre[j-1] == pre[i] - k
。因此,对于每个i
,我们需要统计有多少个j
(其中j
从0
到i
)使得pre[j-1]
等于pre[i] - k
。
为了高效地统计这些 j
的个数,我们使用一个哈希表(如 HashMap
),键是前缀和的值,值是该前缀和出现的次数。这样,对于每个 i
,我们可以直接在哈希表中查找 pre[i] - k
的出现次数,从而避免重复计算。
步骤详解
- 初始化:
- 创建一个哈希表
map
,用于存储前缀和及其出现次数。 - 初始时,放入前缀和
0
,出现次数为1
。这是因为当没有元素时,前缀和为0
,且这相当于j=0
时的pre[j-1] = 0
。 - 初始化当前前缀和
pre = 0
和计数器count = 0
。
- 创建一个哈希表
- 遍历数组:
- 对于每个元素
nums[i]
:- 更新当前前缀和:
pre = pre + nums[i]
。 - 计算目标值
target = pre - k
。这个目标值就是我们要在哈希表中查找的pre[j-1]
。 - 如果
target
存在于哈希表中,则说明存在若干下标j
(对应pre[j-1] = target
),使得子数组[j..i]
的和为k
。因此,将count
加上map.get(target)
。 - 将当前前缀和
pre
加入哈希表:如果pre
已存在,则将其出现次数加1
;否则,放入pre
并设置次数为1
。
- 更新当前前缀和:
- 对于每个元素
- 返回结果:
- 遍历结束后,
count
即为所有和为k
的连续子数组的个数。
- 遍历结束后,
遍历的过程中,我们先求出前缀和,当下标来到i
时,我们求pre[i]-k
出现的次数,即需要统计有多少个 j
(其中 j
从 0
到 i
)使得 pre[j-1]
等于 pre[i] - k
。这些前缀和在我们求pre[i]
前就已经求出。
为什么pre[i] - k
出现的次数等于多少个j
,因为我们从头开始求前缀和,[0i]只会统计一次,当出现相同的前缀和时,是不用的[0i]计算得来的。
代码:
public class Solution {public int subarraySum(int[] nums, int k) {int count = 0, pre = 0;HashMap < Integer, Integer > mp = new HashMap < > ();mp.put(0, 1);for (int i = 0; i < nums.length; i++) {pre += nums[i];if (mp.containsKey(pre - k)) {count += mp.get(pre - k);}mp.put(pre, mp.getOrDefault(pre, 0) + 1);//若当前前缀和不存在,则创建该前缀和,出现次数置为1,若出现过这个前缀和,则该前缀和出现的次数加1}return count;}
}