要求
给你一个整数数组 nums 和一个整数 k ,请你统计并返回 该数组中和为 k 的连续子数组的个数 。
思路
方法一:暴力解
先固定左边界,再去枚举右边
public int subarraySum(int[] nums, int k) {
int count = 0;
int len = nums.length;
for (int left = 0; left < len; left++) {
int sum = 0;
// 区间里可能会有一些互相抵销的元素
for (int right = left; right < len; right++) {
sum += nums[right];
if (sum == k) {
count++;
}
}
}
return count;
}
方法二:前缀和 + 哈希表
我们先了解一下前缀和
我们求数列的和时,Sn = a1+a2+a3+…an; 此时Sn就是数列的前 n 项和。例 S5 = a1 + a2 + a3 + a4 + a5; S2 = a1 + a2。所以我们完全可以通过 S5-S2 得到 a3+a4+a5 的值;我们的前缀和数组里保存的就是前 n 项的和。
我们通过前缀和数组保存前 n 位的和,presum[1]保存的就是 nums 数组中前 1 位的和,也就是 presum[1] = nums[0], presum[2] = nums[0] + nums[1] = presum[1] + nums[1];
例如我们需要获取 nums[2] 到 nums[4] 这个区间的和,我们则完全根据 presum 数组得到
public class LeetCode560 {
public int subarraySum(int[] nums, int k) {
if (nums == null){
return 0;
}
// key:前缀和,value:key 对应的前缀和的个数
HashMap<Integer, Integer> map = new HashMap<>();
//细节,这里需要预存前缀和为 0 的情况,否则会漏掉从第一位开始就满足的情况
//例如输入[1,1,0],k = 2 如果没有这行代码,则会返回0,漏掉了1+1=2,和1+1+0=2的情况
//输入:[3,1,1,0] k = 2时则不会漏掉
//preSum[3] - preSum[0]表示前面 3 位的和,所以需要map.put(0,1),垫下底
map.put(0,1);
//记录需要返回的总数;前缀和
int count = 0,preSum = 0;
for (int num : nums) {
preSum += num;
//当前前缀和已知,判断是否含有preSum - k的前缀和,那么我们就知道某一区间的和为 k 了。
if (map.containsKey(preSum - k)){
//获取次数:map集合中的value
count += map.get(preSum - k);
}
map.put(preSum,map.getOrDefault(preSum,0) + 1);
}
return count;
}
}