【前缀和题目】

前缀和题目

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

前缀和

寻找数组的中心下标

链接

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;
    }
}
相关推荐
Java成神之路-13 分钟前
深入理解 HashMap 扩容时机:JDK 7 vs JDK 8 的设计差异与取舍
java·juc
李冰然13 分钟前
深入Java集合框架:数组与List互转底层原理剖析(JDK 8)
java
竹枝溪23 分钟前
MySQL进阶:约束、多表设计、多表查询与事务
java·数据库·mysql·事务·子查询·acid·多表查询
上玄code31 分钟前
【Agent精讲】调一个LLMAPI背后的工程问题
java·开发语言·python·chatgpt
李冰然32 分钟前
深入Java集合框架:ArrayList源码解析(JDK 8)
java
小小啊python40 分钟前
IDEA中SpringBoot项目配置热部署
java·ide·intellij-idea·springboot·热部署
何以解忧,唯有..44 分钟前
预订酒店问题
算法
No8g攻城狮1 小时前
【算法】什么是 DFS 与 BFS 及他们的区别
算法·深度优先·宽度优先
zww89491111 小时前
家政派单系统实战指南:从需求分析到智能调度算法落地
算法·需求分析
Java成神之路-1 小时前
Spring AI Alibaba 实现多轮对话记忆:ChatMemory 与 Redis 持久化实战
java·springaialibaba