文章目录
前言
Leetcode中涉及前缀和/前缀树的题目较少,不过一遇到就容易GG,也没有人去专门整理这方面的题,本篇来整理Leetcode中的前缀和与前缀树方面的题目与题解。
前缀和题目
560. 和为K的子数组
思路
这道题可以使用O(n^2)的思路,就是让每个nums[i]作为头然后往后累加判断是否加和等于k。但本题可以有更好的思路,可以使用前缀和来解决这道题。
先来看下面这句话:
nums[0]需要的加和有,nums[0],nums[0]+nums[1],nums[0]+nums[1]+nums[2],
nums[1]需要的和有nums[1],nums[1]+nums[2],
nums[2]需要的有nums[2],
而nums[1]+nums[2]可以相当于sum2-sums0,nums[1] = sum1-sum0,nums[2] = sum2-sum1
这样其实只需要遍历一次,同时记录sum数组即可收获所有我们需要计算的值,同时在判断时也可以一次判断,判断是否存在i满足:sum[i]-sum[j]=k,这样就说明j-i之间的值加和等于k,值得注意的是,j不一定是1个值,所以要加进去的次数是sum[j]的次数,而不是加1。
举例:比如[1,2,3,4,-4,4,5],k=5,
其中 sum[3]=10,sum[5]=10,
当sum=15(i=6时),sum-k=10,这时存在两个所以要加两次,也就是说明有两个和可以为5,分别为位置从(3,6]和(5,6]
代码
class Solution {
public int subarraySum(int[] nums, int k) {
int times = 0;
Map<Integer,Integer>map = new HashMap<>();
//防止有从头开始的连续加和直接等于k,比如【1,1,1】,k=2中的前面两个1
map.put(0,1);
int sum = 0;
for(int i=0;i<nums.length;i++)
{
sum += nums[i];
int need = sum-k;
//是否存在sum[j],满足sum[i]-sum[j]=k
if(map.containsKey(need))
times += map.get(need);
//map 中key对应的value可能不是1。
//比如[1,2,3,4,-4,4,5],k=5,sum[3]=10,sum[5]=10,当sum=15(i=6时,sum-k=10,这时存在两个所以要加两次,也就是说明有两个和可以为5)
//分别为位置从(3,6]和(5,6]
map.put(sum,map.getOrDefault(sum,0)+1);
}
return times;
}
}
5248. 统计「优美子数组」
思路
本题和上一题类似,题目中要统计奇数个数为k的子数组个数,可以将奇数置为1,偶数置为0,这样就变成了统计和为k的子数组个数,就与上一道题相同了。
代码
class Solution {
public int numberOfSubarrays(int[] nums, int k) {
//使用前缀和的原因也是子数组要连续
//使用前缀和(将奇数变为1,偶数为0)
//sum[i] = sum[i-1]+num;
//如果存在sum[i]-k>0,那就说明肯定存在一段数组的和等于k
//如果从(j,n]的和为k ,那么当我们找到sum[n]的时候,就会存在sum[n]-k=sum[j],此时times就加上sum[j]的个数,
//因为这里j可能不是一个值,
Map<Integer,Integer>map = new HashMap<>();
int sum = 0;
map.put(0,1);
int temp = 0;
for(int i=0;i<nums.length;i++)
{
if(nums[i]%2!=0)
{
sum += 1;
}
if(sum>=k)
{
//和为j
int j = sum-k;
//找和为j的个数
temp+=map.get(j);
}
map.put(sum,map.getOrDefault(sum,0)+1);
}
return temp;
}
}