和为k的子数组
继续每日一题,今天给大家分享一道经典的前缀和问题
先看一下题目,我带着大家通过一道题目去分析前缀和的思想和解决思路
题目描述:
给你一个整数数组 nums 和一个整数 k ,请你统计并返回 该数组中和为 k 的子数组的个数 。
子数组是数组中元素的连续非空序列。
题目示例:
前缀和定义为从数组起始位置到当前位置(包括当前位置)的所有元素之和。
我们使用前缀和的思想。前缀和定义为从数组起始位置到当前位置(包括当前位置)的所有元素之和。
假设我们有一个数组 nums,那么前缀和数组 pre 的第 i 个元素 pre[i] 就是 nums[0] 到 nums[i] 的和。
那么,针对任意子数组(从下标 i 到 j,j>i)的和可以表示为 pre[j] - pre[i-1](这里如果i=0,则pre[-1]我们定义为0,即没有元素时和为0)。
p r e [ i ] = n u m s [ 0 ] + n u m s [ 1 ] + . . . + n u m s [ i ] p r e [ j ] = n u m s [ 0 ] + n u m s [ 1 ] + . . . + n u m s [ i ] + . . . + n u m s [ j ] pre[i]=nums[0]+nums[1]+...+nums[i]\\ pre[j]=nums[0]+nums[1]+...+nums[i]+...+nums[j] pre[i]=nums[0]+nums[1]+...+nums[i]pre[j]=nums[0]+nums[1]+...+nums[i]+...+nums[j]
任意子数组i->j的和则是:nums[i+1]+nums[i+2]+…+nums[j] 即:
p r e [ j ] − p r e [ i ] pre[j]-pre[i] pre[j]−pre[i]
我们的目标是找到所有满足子数组和为 k 的情况,即:
p r e [ j ] − p r e [ i ] = k pre[j] - pre[i] = k pre[j]−pre[i]=k
这里注意:pre[i]对应的是从0到i的和,那么从i+1到j的子数组和为pre[j]-pre[i])
但是,我们也可以看成:对于每个 j(当前遍历到的元素),我们要找有多少个 i(i < j)满足
p r e [ j ] − p r e [ i ] = k pre[j] - pre[i] = k pre[j]−pre[i]=k
因此,我们可以遍历数组,计算每个位置的前缀和,然后检查在当前位置之前,有多少个位置的前缀和等于当前前缀和减去k。
为了快速查找,我们使用一个哈希表来记录之前出现过的前缀和以及它们出现的次数。
因为题目给的case有点简单,我们本次不在以题目中的case为例子去演示
nums = [3, 4, 7, 2, -3, 1, 4, 2],k = 7
定义:j为当前遍历到的元素,我们按照上面的分析过程,需要找到一个或多个i使其满足:
p r e [ i ] = p r e [ j ] − k pre[i]=pre[j]-k pre[i]=pre[j]−k
当前遍历的元素j | pre[j] | pre[j]-k | 遍历到j时,map里的元素 | 子数组数 |
---|---|---|---|---|
j=0 | pre[0]=nums[0]=3 | 3-7=-4 哈希表中无-4 | {0:1} | 0 |
j=1 | pre[1]=nums[0]+nums[1]=3+4=7 | 7-7=0 哈希表中有0 | {0:1,3:1} | 0+1=1 |
j=2 | pre[2]=nums[0]+nums[1]+nums[2]=3+4+7=14 | 14-7=7 哈希表中有7 | {0:1,3:1,7:1} | 1+1=2 |
j=3 | pre[3]=nums[0]+nums[1]+nums[2]+nums[3]=3+4+7+2=16 | 16-7=9 哈希表中无9 | {0:1,3:1,7:1,14:1} | 2 |
j=4 | pre[4]=nums[0]+nums[1]+…+nums[4]=3+4+7+2-3=13 | 13-7=6 哈希表中无6 | {0:1,3:1,7:1,14:1,16:1} | 2 |
j=5 | pre[5]=nums[0]+nums[1]+…+nums[5]=3+4+7+2-3+1=14 | 14-7=7 哈希表中有7 | {0:1,3:1,7:1,14:1,16:1,13:1} | 2+1=3 |
j=6 | pre[6]=nums[0]+nums[1]+…+nums[6]=3+4+7+2-3+1+4=18 | 18-7=11 哈希表中无11 | {0:1,3:1,7:1,14:2,16:1,,13:1} | 3 |
j=7 | pre[7]=nums[0]+nums[1]+…+nums[7]=3+4+7+2-3+1+4+2=20 | 20-7=13 哈希表中有13 | {0:1,3:1,7:2,14:2,16:1,,13:1,18:1} | 3+1=4 |
j=8 | - | - | {0:1,3:1,7:2,14:2,16:1,,13:2,18:1,20:1} | 4 |
所以子数组个数为2
理解了以上过程,代码实现就很简单了:
public static int subarraySum(int[] nums, int k) {Map<Integer, Integer> prefixMap = new HashMap<>();//初始化前缀表prefixMap.put(0, 1);int sum = 0;int count = 0;for (int j = 0; j < nums.length; j++) {sum += nums[j];int i = prefixMap.getOrDefault(sum - k, 0);if (i > 0) {//说明存在prefix[i]count += i;}prefixMap.put(sum, prefixMap.getOrDefault(sum, 0) + 1);}return count;}