Given an array of integers nums
and an integer k
, return the total number of continuous subarrays whose sum equals to k
.
Example 1:
Input: nums = [1,1,1], k = 2
Output: 2
Example 2:
Input: nums = [1,2,3], k = 3
Output: 2
Constraints:
1 <= nums.length <= 2 * 104
-1000 <= nums[i] <= 1000
-107 <= k <= 107
解法一:(暴力破解)
都会,代码略
解法二:(利用累加和)
这个解法利用的是,如果有a + k = b,那么就必然有b - a = k的原理。a和b为别为数组某两位之前的数字累加和。换句话说,设sum[i]和sum[j] (j>i)分别是数组第i位之前和第j位之前的数字累加和,那么如果有sum[j] - sum[i] == k,那就说明从第i位到第j位上的数字加和等于k。
具体算法:
声明一个Hashmap,key为数组累加和出现的值,value为该值在累加过程中出现的次数。
以[3,4,7,2,-3,1,4,3] k = 7为例,计算过程如下
nums[i] | 3 | 4 | 7 | 2 | -3 | 1 | 4 | 3 | |
sum[i] (到第i位为止的累加和) |
0 | 3 | 7 | 14 | 16 | 13 | 14 | 18 | 21 |
hashmap key (到第i位为止出现过的累加和的值) |
0 | 3 | 7 | 14 | 16 | 13 | 14 | 18 | 21 |
hashmap value (到第i位为止出现过的累加和的值的对应次数) |
1 | 1 | 1 | 1 | 1 | 1 | 2 | 1 | 1 |
count = hashmap(sum[i] - k) + 1 |
0 | - | 1 | 2 | - | - | 3 | - | 5 |
在上述的计算过程中,第i位之前的累加值两次出现14,在第一次出现14的时候因为14-k = 7,而7恰好在之前出现过一次,那么count就在7出现的次数上加1,得到2。也就是说,在数组遍历到数字7的时候,前面已经有两个数字(3,4)可以组成符合条件的子数组了,那么加上现在的7以后,就可以独立组成另外一组子数组,也就是[(3,4), (7)];而后当sum[i]第二次出现14的时候,14-k继续等于7,那么继续在现有count基础之上加上当前map中7出现的次数,也就是1,得到当前新的count数。也就是说当数组遍历到数字1的时候,在它之前已经有2个符合条件的子数组出现过了,就是刚才的[(3,4), (7)],那么加上现在这个当前值1,就组成了第三个符合条件的数组,即[(3,4), (7),(7,2,-3,1)]。当遍历的最后一个数字3的时候,sum[i]==21,但是21-k == 14,而14又在前面出现过2次,说明从数组最后一位到最近一次出现累加和等于14的位置上,存在符合条件的子数组(4,3),但是为什么这里要把count+2而不是像之前一样+1呢?因为在最近一次(也就是第二次)出现累加和等于14之前,到第一次出现14之间的数字加和是0,因为0和任意数组组合都符合题意要求,所以需要把这次加上。这也就是hashmap要记录每个累加和出现次数的原因,因为只要累加和曾经出现过,那么两次累加和之间的数字加和就一定是0。(否则怎么可能两次累积和的数字一样呢?)
总结一下上面的过程就是,如果在遍历数组的过程中发现sum[i] - k的值曾经出现过,那么就意味着有一个新的符合条件的数组出现,即从第i位到它之前的某一位,但具体是哪一位我们其实不用关心。如果非要关心的话,就是从当前第i位到上一次出现sum[i] - k的值的位置,比如当第二次遇到sum[i] == 14的时候,符合条件的子数组就是从当前位第5位到上一次sum[i] == 14(即第一次sum[i]==14)的时候(i=2),它们之间的数字(7,2,-3,1)。
public static int subarraySum(int[] nums, int k) {
int count = 0, sum = 0;
HashMap map = new HashMap<>();
map.put(0, 1);
for (int i = 0; i < nums.length; i++) {
sum += nums[i];
if (map.containsKey(sum - k))
count += map.get(sum - k);
map.put(sum, map.getOrDefault(sum, 0) + 1);
}
return count;
}