题目描述:
给定一个整数数组和一个整数 k,你需要找到该数组中和为 k 的连续的子数组的个数。
示例 1 :
输入:nums = [1,1,1], k = 2
输出: 2 , [1,1] 与 [1,1] 为两种不同的情况。
说明 :
数组的长度为 [1, 20,000]。
数组中元素的范围是 [-1000, 1000] ,且整数 k 的范围是 [-1e7, 1e7]。
题目解析:
方法一:注意题目给出的是找和为k连续的子数组的个数,然后发现数组长度为[1,20000],所以我们可以直接暴力,枚举其全部情况,然后判断与k相等的情况有多少。
**方法二:**前缀和+哈希,我们假设用pre数组来存储前缀和,那么不难得出 pre[i] = pre[i-1] + nums[i],那我们如果通过前缀和来得到一个连续的区间和应该怎么做呢?假设在区间[j,i],那么我们可以得出区间和为pre[i] - p[j - 1],所以我们就可以转换成存在几个区间和为k的情况,得到pre[i] - p[j - 1]==k,移动一下就得到 pre[j - 1] ==pre[i] - k,所以我们只需要每次去判断是否pre[i]-k存在即可。详见代码。
代码:
public class LC560 {
//暴力 时间复杂度O(n^2) 空间复杂度O(1)
//因为题目要求的是连续的子数组,所以我们可以枚举全部情况,然后判断与k相等的情况有多少。
public int subarraySum(int[] nums, int k) {
//计数器,记录子集和为k的个数
int cnt = 0;
//i+1代表目前集合中元素的最多个数
for (int i = 0; i < nums.length; i++){
//中间和
int sum = 0;
//j倒着来,这样我们可以计算添加一个新元素后,比之前新增了多少个连续的和
for (int j = i; j >= 0; j--){
sum += nums[j];
if (sum == k){
cnt++;
}
}
}
return cnt;
}
//方法二:前缀和,时间复杂度O(n) 空间复杂度O(n)
public int subarraySum1(int[] nums,int k){
int cnt = 0, pre = 0;
//和为key 出现次数为value
HashMap<Integer, Integer> map = new HashMap<>();
//不可缺少,因为pre-k==0时刚好是我们要找的情况
map.put(0,1);
for (int num : nums){
//我们可以省去数组,直接用pre来代表前缀和
pre += num;
//判断是否哈希mp中是否存在pre-k
if (map.containsKey(pre - k)){
cnt += map.get(pre - k);
}
map.put(pre,map.getOrDefault(pre,0) + 1);
}
return cnt;
}
public static void main(String[] args) {
LC560 obj = new LC560();
System.out.println(obj.subarraySum(new int[]{1, 3, 2, 4, 3}, 6));
}
}