560. 和为K的子数组
给你一个整数数组 nums 和一个整数 k ,请你统计并返回 该数组中和为 k 的子数组的个数 。
子数组是数组中元素的连续非空序列。
示例 1:
输入:nums = [1,1,1], k = 2
输出:2
解题思路:
这道题求的是子数组和为k,看到子数组和就要想到前缀和之差,即pre_sum[i] - pre_sum[j] = k.
所以就需要先计算出前缀和数组pre_sum,然后遍历i,然后再遍历j,找到所有即i结尾的并且和为k的子数组。
class Solution {public int subarraySum(int[] nums, int k) {int n = nums.length;int ans = 0;int[] pre_sum = new int[n]; // 记录前缀和pre_sum[0] = nums[0];for(int i = 1; i < n; i++){pre_sum[i] = pre_sum[i-1] + nums[i];}for(int i = 0; i < n; i++){// 如果pre_sum[i]等于k也要加一if(pre_sum[i] == k){ans++;}for(int j = 0; j < i; j++){if(pre_sum[i] - pre_sum[j] == k){ans++;}}}return ans;}
}
优化:
通过分析代码可以知道,对于每个i,j都要从头开始遍历一遍,浪费了大量时间。如果我们能在O(1)的时间内就知道 i 前面有哪些前缀和,就能节省时间。那么就自然想到用HashMap记录下 i 前面有哪些前缀和以及出现的次数。
并且在map里面添加一个key为0的元素,这样就不用单独判断当pre_sum=k的情况了。
class Solution {public int subarraySum(int[] nums, int k) {int n = nums.length;int ans = 0;int[] pre_sum = new int[n]; // 记录前缀和pre_sum[0] = nums[0];Map<Integer, Integer> cnt = new HashMap<>();for(int i = 1; i < n; i++){pre_sum[i] = pre_sum[i-1] + nums[i];}cnt.put(0, 1);for(int i = 0; i < n; i++){ans += cnt.getOrDefault(pre_sum[i] - k, 0);cnt.put(pre_sum[i], cnt.getOrDefault(pre_sum[i], 0) + 1);}return ans;}
}
优化:
我们可以发现,两次for循环其实可以合并为一个for循环,并且我们已经用HashMap记录了 i 之前的前缀和了,那么就可以不用单独用数组来存储前缀和了。
class Solution {public int subarraySum(int[] nums, int k) {int ans = 0;int sum = 0;Map<Integer, Integer> cnt = new HashMap<>();cnt.put(0, 1);for(int num : nums){sum += num;ans += cnt.getOrDefault(sum - k, 0);cnt.put(sum, cnt.getOrDefault(sum, 0) + 1);}return ans;}
}