【前缀和题目】

前缀和题目

前缀和技巧适用于快速、频繁地计算一个索引区间内的元素之和。

前缀和

寻找数组的中心下标

链接

java 复制代码
class Solution {
    public int pivotIndex(int[] nums) {
        //思路:1.求出前缀和 2.遍历前缀和
        int sz=nums.length;
        int[]preSum=new int[sz+1];//preSum[i]:前i个的和
        for(int i=1;i<=sz;i++){
            preSum[i]=preSum[i-1]+nums[i-1];
        }
        
        for(int i=1;i<=sz;i++){
            int leftSum=preSum[i-1];
            int rightSum=preSum[sz]-preSum[i];
            if(leftSum==rightSum){
                //找到了
                return i-1;
            }
        }
        return -1;
    }
}

前缀积

除自身以外数组的乘积

链接

java 复制代码
class Solution {
    public int[] productExceptSelf(int[] nums) {
        int sz=nums.length;
        //思路:求出前缀积和后缀积
        int[]preMuti=new int[sz+2];//preMuti[i]:前一个的累积
        int[]backMuti=new int[sz+2];
        Arrays.fill(preMuti,1);
        Arrays.fill(backMuti,1);
        for(int i=1;i<=sz;i++){
            preMuti[i]=preMuti[i-1]*nums[i-1];
        }
        for(int i=sz;i>=1;i--){
            backMuti[i]=backMuti[i+1]*nums[i-1];
        }
        int[]res=new int[sz];
        for(int i=1;i<=sz;i++){
            res[i-1]=preMuti[i-1]*backMuti[i+1];
        }
        return res;
    }
}

前缀和+哈希表

涉及到和为 xxx 的子数组 ,就是要考察 前缀和技巧 和 哈希表 的结合使用了。

连续数组

链接

java 复制代码
class Solution {
    public int findMaxLength(int[] nums) {
        //可以转换成求子数组和为xxx的最长子数组
        //1.把0变成-1,求子数组和为0的最长子数组长度
        int sz=nums.length;
        for(int i=0;i<sz;i++){
            if(nums[i]==0){
                nums[i]=-1;
            }
        }
        //2.求前缀和
        int[]preSum=new int[sz+1];
        for(int i=1;i<=sz;i++){
            preSum[i]=preSum[i-1]+nums[i-1];
        }
        //3.在前缀和数组中,寻找preSum[i]==preSum[j],i和j越远越好
        //用到了哈希表,在遍历前缀和数组时,第一次遇到就进去,第二次遇到就更新
        //i越小越好
        int res=0;//记录最长的子数组
        HashMap<Integer,Integer>map=new HashMap();//key是值,value是索引
        for(int i=0;i<=sz;i++){
            int cur=preSum[i];
            if(!map.containsKey(cur)){//说明是第一次出现
                map.put(cur,i);
            }else{
                //前面出现过cur
                res=Math.max(res,i-map.get(cur));
            }

        }
        return res;
    }
}

连续的子数组和

链接

寻找 i, j 使得 (preSumi - preSumj) % k == 0 且 i - j >= 2。

另外,(preSumi - preSumj) % k = = 0 其实就是 preSumi % k = = preSumj % k。

java 复制代码
class Solution {
    public boolean checkSubarraySum(int[] nums, int k) {
        //先求出前缀和 前缀和模上k
        int sz=nums.length;
        int[] preSum=new int[sz+1];
        for(int i=1;i<=sz;i++){
            preSum[i]=(preSum[i-1]+nums[i-1])%k;
        } 
        //key是出现的值,value是索引
        HashMap<Integer,Integer> map=new HashMap();
        map.put(0,0);
        for(int i=1;i<=sz;i++){
            int cur=preSum[i];
            if(map.containsKey(cur)){
                //前面出现了cur,长度也>=2
                if(i-map.get(cur)>=2){
                    return true;
                }
            }else{
                map.put(cur,i);
            }
        }
        return false;
    }
}

和为k的子数组

链接

java 复制代码
class Solution {
    public int subarraySum(int[] nums, int k) {
        int sz=nums.length;
        //前缀和
        int[]preSum=new int[sz+1];
        for(int i=1;i<=sz;i++){
            preSum[i]=preSum[i-1]+nums[i-1];
        }
        int counter=0;
        //key是值,value是出现的个数
        HashMap<Integer,Integer> map=new HashMap();
        map.put(0,1);
        for(int i=1;i<=sz;i++){
            int cur=preSum[i];
            if(map.containsKey(cur-k)){
                //map中包含cur-k
                counter+=map.get(cur-k);
            }
            map.put(cur,map.getOrDefault(cur,0)+1);
        }
        return counter;
    }
}

和可被k整除的子数组

链接

我的错误思路:1.先求出前缀和数组,然后每个值对k取模 2.找出preSumi==preSumj 的个数。

出现的问题:当nums为{-1,2,9} ,k=2,preSum={0,-1,1,0},所以答案为1{-1,2,9},漏了{2}。

我们求
preSum nums\[j...i ] % k= =0 ,转换成
(preSumnums\[0...i]-preSUmnums\[0...j] )%k ==0 ,

转换成:
preSumnums\[0...i]%k= =preSumnums\[0...j]%k .

回到上面的例子,-1和1也是同余关系,我们应该把负数变成正数即可。

java 复制代码
import java.util.HashMap;

class Solution {
    public int subarraysDivByK(int[] nums, int k) {
        int sz=nums.length;
        int[]preSum=new int[sz+1];
        for(int i=1;i<=sz;i++){
            preSum[i]=preSum[i-1]+nums[i-1];
        }
        for(int i=0;i<=sz;i++){
            preSum[i]%=k;
        }
        //在preSum中找一样的
        int counter=0;
        //key表示同余的值全是整数,如果是负数就变成整数,value是个数
        HashMap<Integer,Integer> map=new HashMap();
        map.put(0,1);
        for(int i=1;i<=sz;i++){
            int cur=preSum[i]%k;
            if(cur<0){
                cur+=k;
            }
            if(map.containsKey(cur)){
                counter+=map.get(cur);
            }
            map.put(cur,map.getOrDefault(cur,0)+1);
        }
        return counter;
    }
}

表现良好的最长时间段

链接

java 复制代码
import java.util.HashMap;

class Solution {
    public int longestWPI(int[] hours) {
        //可以把>8小时的一天赋值为1,<=8的为-1
        //求和>0的最长数组
        int sz=hours.length;
        for(int i=0;i<sz;i++){
            if(hours[i]>8){
                hours[i]=1;
            }else{
                hours[i]=-1;
            }
        }
        //前缀和
        int[]preSum=new int[sz+1];
        for(int i=1;i<=sz;i++){
            preSum[i]=preSum[i-1]+hours[i-1];
        }
        //key=前缀和的值,value是索引
        HashMap<Integer,Integer> map=new HashMap();
        map.put(0,0);
        int maxPeriod=0;
        //遍历前缀和数组
        for(int i=1;i<=sz;i++){
            int curPreSum=preSum[i];
            if(curPreSum>0){
                //curPreSum为正数,说明前面[0,i-1]是良好时间段
                maxPeriod=Math.max(maxPeriod,i);
            }else{
                //curPreSum为负数,想要最长的时间段
                if(map.containsKey(curPreSum-1)){
                    //找到了
                    maxPeriod=Math.max(maxPeriod,i-map.get(curPreSum-1));
                }
            }
            
            //如果当前的值前面没有出现过,就记录下来,出现过就别管,前缀索引越小越好
            if(!map.containsKey(curPreSum)){
                map.put(curPreSum,i);
            }
        }
        return maxPeriod;
    }
}
相关推荐
不像程序员的程序媛4 小时前
mybatisplus insert dto未指定id时会如何
java·开发语言
零基础1234 小时前
基于模型的强化学习与不基于模型的强化学习:核心区别与实战选择
python·算法
我命由我123454 小时前
5-2.Python 编码进阶 - 字符串进阶(拼接、普通格式化、普通格式化数字精度控制、快速格式化、遍历、常用方法)
java·开发语言·后端·python·学习·java-ee·学习方法
Pniubi12 小时前
HashMap的扩容和树化边界
java·开发语言
Mikko712 小时前
用 Java 复刻了高中时在塞班上玩的「熔岩矿车」:纯 Swing、零依赖、没有一张图片,一个 jar 双击就能玩
java·游戏开发·swing
知了学历孙老师12 小时前
小自考的校考和统考,出题方和难度差在哪
java·大数据·前端
Java爱好狂.13 小时前
Java初学者如何一周速通SpringBoot!
java·spring boot·后端·spring·高并发·架构师·java面试
秋饼13 小时前
LLM API密钥动态轮换实战:Spring Vault + Spring AI + 多Provider路由
java·ai·技术分享·后端开发
福兮说15 小时前
JSON 格式正确,数据就可靠吗?我用一份自编配置检查类型与空值
java·前端·json
罗西的思考15 小时前
【Agentic RL】Dressage Staleness 控制深度分析
人工智能·算法·机器学习