【前缀和题目】

前缀和题目

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

前缀和

寻找数组的中心下标

链接

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;
    }
}
相关推荐
考虑考虑3 分钟前
JDK9中的dropWhile
java·后端·java ee
想躺平的咸鱼干11 分钟前
Volatile解决指令重排和单例模式
java·开发语言·单例模式·线程·并发编程
CVer儿22 分钟前
svd分解求旋转平移矩阵
线性代数·算法·矩阵
Owen_Q30 分钟前
Denso Create Programming Contest 2025(AtCoder Beginner Contest 413)
开发语言·算法·职场和发展
hqxstudying38 分钟前
java依赖注入方法
java·spring·log4j·ioc·依赖
·云扬·1 小时前
【Java源码阅读系列37】深度解读Java BufferedReader 源码
java·开发语言
Wilber的技术分享2 小时前
【机器学习实战笔记 14】集成学习:XGBoost算法(一) 原理简介与快速应用
人工智能·笔记·算法·随机森林·机器学习·集成学习·xgboost
Tanecious.2 小时前
LeetCode 876. 链表的中间结点
算法·leetcode·链表
Bug退退退1232 小时前
RabbitMQ 高级特性之重试机制
java·分布式·spring·rabbitmq
小皮侠2 小时前
nginx的使用
java·运维·服务器·前端·git·nginx·github