前缀和
一、思路概述
通过初次遍历统计数组中前n个数的和,以便后续算法使用,通常使用情景为要求数组中某一子数组(连续的子序列)的和符合某些条件,比如计算和为正数的最大子区间。
前缀和只是对于数据的初次整理,具体如何利用还是得看实际题目
二、题目举例
给你一个正整数数组 nums,请你移除 最短 子数组(可以为 空),使得剩余元素的 和 能被 p 整除。 不允许 将整个数组都移除。
请你返回你需要移除的最短子数组的长度,如果无法满足题目要求,返回 -1 。
来源:力扣(LeetCode)
链接:https://leetcode.cn/problems/make-sum-divisible-by-p
三、思路分析
1、关键字提取
a、子数组求和 b、被p整除
a.这意味着我们需要使用前缀和算法来减少对子数组求和这一过程的时间,
b.被p整除意味着余数为0,故而对于每一个数组元素i来说,只需要保留i%p就可以了,
因为对于取余操作有a%p+b%p=(a+b)%p(想不通的话另p=10,按照正常加法思路就知道了)
2、前缀和有什么用
前缀和一个很经典的用法是求有一个区间内部元素和,通过构建前缀和数组pre,我们已经知道了整个数组元素的和pre[nums.length],现在是删除某一个区间,也就是选择区间【i,j】,从pre[nums.length]中删去pre[j]-pre[i]使得二者之差对p取余为0,此时用到另一个思维方法,双指针即可快速获取目标
四、详细代码
public int minSubarray(int[] nums, int p) {
int x=0;
for(int num:nums)
{
x=(x+num)%p;
}
if(x==0) return 0;
int ans=nums.length;
int y=0;
int target=0;
HashMap<Integer,Integer> pre=new HashMap<Integer,Integer>(Math.min(p,nums.length));
for(int i=0;i<nums.length;i++)
{
pre.put(y,i);
y=(y+nums[i])%p;
target=(y-x+p)%p;
if(pre.containsKey(target))
{
ans=Math.min(ans,i-pre.get(target)+1);
}
}
return ans==nums.length?-1:ans;
}
其中有几个值得注意的点(默认的前缀和数据都对p取余过了)
1、HashMap中存放的余数为y时候对应的最右边的前缀和的下标,也就是左指针,i对应的是右指针,在最后比对的时候,如果存在两个相同的前缀和,那么依据hashMap的性质,左指针的下标会被往跟大的方向更新,从而,满足删除最少元素的需求
2、target对应的是y-x的差值,其中y代表当前的前缀和,x代表整个数组的前缀和,之所以要加上p是为了防止y比x小的情况出现
3、HashMap如果预定一个初始容量的话就可以避免过多次的变化大小,一定程度上加快程序运行,所以最好还是预设一个初始容量