【前缀和题目】

前缀和题目

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

前缀和

寻找数组的中心下标

链接

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;
    }
}
相关推荐
workflower3 小时前
单元测试-例子
java·开发语言·算法·django·个人开发·结对编程
YuanlongWang3 小时前
C# 基础——装箱和拆箱
java·开发语言·c#
b78gb3 小时前
电商秒杀系统设计 Java+MySQL实现高并发库存管理与订单处理
java·开发语言·mysql
wb043072014 小时前
性能优化实战:基于方法执行监控与AI调用链分析
java·人工智能·spring boot·语言模型·性能优化
MicroTech20255 小时前
微算法科技(MLGO)研发突破性低复杂度CFG算法,成功缓解边缘分裂学习中的掉队者问题
科技·学习·算法
天若有情6735 小时前
Java Swing 实战:从零打造经典黄金矿工游戏
java·后端·游戏·黄金矿工·swin
墨染点香6 小时前
LeetCode 刷题【126. 单词接龙 II】
算法·leetcode·职场和发展
aloha_7896 小时前
力扣hot100做题整理91-100
数据结构·算法·leetcode
lichong9516 小时前
Android studio 修改包名
android·java·前端·ide·android studio·大前端·大前端++
lichong9516 小时前
Git 检出到HEAD 再修改提交commit 会消失解决方案
java·前端·git·python·github·大前端·大前端++