【前缀和题目】

前缀和题目

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

前缀和

寻找数组的中心下标

链接

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 使得 (preSum[i] - preSum[j]) % k == 0 且 i - j >= 2。

另外,(preSum[i] - preSum[j]) % k = = 0 其实就是 preSum[i] % k = = preSum[j] % 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.找出preSum[i]==preSum[j] 的个数。

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

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

转换成:
preSum[nums[0...i]]%k= =preSum[nums[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 分钟前
Spring Boot + CRaC 启动速度提升了10倍!
java·spring boot·后端
Joyner20186 分钟前
python-leetcode-压缩字符串
算法·leetcode·职场和发展
雷渊12 分钟前
深入分析mysql给表加字段涉及到的锁
java·后端·面试
独立开阀者_FwtCoder16 分钟前
用 DeepSeek 构建 Vue.js 底层架构:高效协作与问题解决实践
java·前端·算法
这名字应该不会重复吧20 分钟前
neo4j中常用cql命令汇总(基础版)
java·neo4j
qw94920 分钟前
SpringCloud——LoadBalancer负载均衡服务调用
java·spring cloud·负载均衡
草药味儿の岁月30 分钟前
Spring Boot启动流程及源码实现深度解析
java·spring boot·后端
AI技术控41 分钟前
深度学习算法实战——增强现实(AR)——主页有源码
深度学习·算法·ar
Akamai中国1 小时前
Ateme在云端构建可扩展视频流播平台
网络·算法·云原生·云计算·音视频·云服务·云存储
失乐园1 小时前
Java 新特性全解析:从 Lambda 到虚拟线程,一文掌握最新演进
java·后端·面试