【视频】30分钟掌握前缀和,你能做到吗?

本文详细介绍了如何利用前缀和解决数组和子数组相关问题,包括二分查找、哈希表等方法。通过实例解析了209.长度最小的子数组、523.连续的子数组和、525.连续数组、974.和可被K整除的子数组等题目,展示了前缀和在动态规划和数组操作中的应用。同时,文章还涉及了除自身以外数组的乘积计算和相同元素的间隔之和问题,提供了多种算法思路和代码实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

视频指路

https://www.bilibili.com/video/BV1iq4y117xZ?spm_id_from=333.999.0.0

算法专题精进 掌握前缀和

刚好30分钟,这个视频主要讲了小松在过去一周对前缀和的梳理,是用34寸屏幕录的,所以看起来有点窄,另外不知道为什么感觉有时不时的破音,下次用笔记本录吧~

下文是这期视频的源代码和一些总结,不过需要先看完视频后才能看懂哦

二分

209. 长度最小的子数组
class Solution {
    public int minSubArrayLen(int target, int[] nums) {
        // 连续子数组 的运算结果
        int [] preSum = new int[nums.length + 1];
        for(int i = 1 ; i < preSum.length; i++){
            preSum[i] = preSum[i - 1] + nums[i - 1];
        }
        /**
         nums     [2,3,1,2,4,3]
         preSum   [0,2,5,6,8,12,15]
         preSum[y] - preSum[x] >= target 
         preSum[x] + target <= preSum[y]; 
         x < y 
         0 - > len
         x = i

         // 前缀和递增时,可以使用二分查找
         */
         int res = Integer.MAX_VALUE;
         for(int i = 0; i < preSum.length; i++ ){
            int targetNum = target + preSum[i];
            int upBound = Arrays.binarySearch(preSum, targetNum);
            if(upBound < 0)upBound = -upBound - 1;
            if(upBound < preSum.length)res = Math.min(res, upBound - i);

         }
         return res == Integer.MAX_VALUE ? 0: res;

    }
}

哈希

[523. 连续的子数组和
class Solution {
    public boolean checkSubarraySum(int[] nums, int k) {
        int [] preSum = new int[nums.length + 1];
        for(int i = 1; i < preSum.length; i++){
            preSum[i] = preSum[i - 1] + nums[i - 1];
        }

        // 同余定理,两个前缀和对k 取余的余数一样 k=  5  6 -> 1 11 - > 1
        // 差 是k的倍数  
        // 记忆化
        // [23,2, 4, 6,6 ]
        // [0,23,25,29,35,41]
        Map<Integer, Integer>map = new HashMap<>();// 余数,i
        for(int i = 2; i < preSum.length; i++){
            int number = preSum[i - 2] % k;
            map.put(number, i - 2);
            if(map.containsKey(preSum[i] % k)){
                return true;
            }
        }
        return false;
    }
}
525. 连续数组
class Solution {
    public int findMaxLength(int[] nums) {
        // 记录counter 放入map中,如果下次遇到相同的counter,表示在两次counter之间, 0,1相同
      // map记录之前的位置
        int count = 0;
        int maxMumber = 0;
        Map<Integer, Integer>map = new HashMap<>();
        map.put(count, -1);
        for(int i = 0; i < nums.length; i++){
            if(nums[i] == 1)count++;
            else count --;
            if(map.containsKey(count)){
                int preIndex = map.get(count);
                maxMumber = Math.max(maxMumber, i - preIndex);
            }else{
                map.put(count, i);
            }
        }
        return maxMumber;
    }
}
974. 和可被 K 整除的子数组

同余定理

class Solution {
    public int subarraysDivByK(int[] nums, int k) {
        // 构造前缀和
        int [] preSum = new int[nums.length + 1];
        for(int i = 1; i < preSum.length; i++){
            preSum[i] = preSum[i - 1] + nums[i - 1];
            System.out.print(preSum[i] + " ");

        }
        System.out.println();
        /**
        // 面对含负数的数组?前缀和不再单调
            [4,5,0,-2,-3,1]
            [0,4,9, 9, 7,4,5]
         */
        int res = 0;
        // 找边界
        Map<Integer, Integer>map = new HashMap<>();
        map.put(0, 1);// 余数为0 直接先加一次
        for(int i = 1; i < preSum.length; i++){
            int modulus = (preSum[i] % k + k) % k;
            int same = map.getOrDefault(modulus, 0); // 相同余数,证明两个前缀和之差能被5整除
            res += same;
            map.put(modulus, same + 1);
        }
        return res;
    }
          // 5     1
        // 0     2
        // -2-3  3
}

238. 除自身以外数组的积
class Solution {
    public int[] productExceptSelf(int[] nums) {
        int [] preSum = new int[nums.length + 1];
        int [] oreSum = new int[nums.length + 1];
        preSum[0] = 1;
        preSum[1] = 1;
        oreSum[nums.length] = 1;
        oreSum[nums.length - 1] = 1;
        for(int i = 1; i < nums.length; i++){
            preSum[i + 1] = preSum[i] * nums[i - 1];
        }
        for(int i = nums.length - 2; i >= 0; i--){
            oreSum[i] = oreSum[i + 1] * nums[i + 1];
        }
        int [] res = new int[nums.length];
        for(int i = 0; i < nums.length; i++){
            res[i] = preSum[i + 1] * oreSum[i] ;
        }
        return res;
    }
}
2121. 相同元素的间隔之和
class Solution {
    public long[] getDistances(int[] arr) {
        //前缀,<key,val>表示值为key的前面一个相同的下标为val[0],相同的个数为val[1]
        Map<Integer, int[]> m1 = new HashMap<>();
        int n = arr.length;
        long[] re1 = new long[n];
        for (int i = 0; i < n; i++) {
            int[] orDefault = m1.getOrDefault(arr[i], new int[2]);
            //相同的下标为ordefaule[0],相同了几个为orderfault[1]
            if (orDefault[1] != 0) {
                re1[i] += re1[orDefault[0]] + (i - orDefault[0]) * orDefault[1];
            }
            orDefault[0] = i;
            orDefault[1]++;
            m1.put(arr[i], orDefault);
        }
        //后缀
        Map<Integer, int[]> m2 = new HashMap<>();
        long[] re2 = new long[n];
        for (int i = n - 1; i >= 0; i--) {
            int[] orDefault = m2.getOrDefault(arr[i], new int[2]);
            //当其后面有与他下相同的时候。相同的下标为ordefaule[0],相同了几个为orderfault[1]
            if (orDefault[1] != 0) {
                re2[i] += re2[orDefault[0]] + (orDefault[0] - i) * orDefault[1];
            }
            orDefault[0] = i;
            orDefault[1]++;
            m2.put(arr[i], orDefault);
        }
        long[] re = new long[n];
        for (int i = 0; i < n; i++) {
            re[i] = re1[i]+re2[i];
        }
        return re;
    }
}

前缀和

涉及到连续子数组,就可以用

可以先直接写框架

如果前缀和单调递增,可以二分求边界

如果不能使用二分,可以使用hash记忆化

后缀同样适用

框架代码

class Solution {
    public boolean checkSubarraySum(int[] nums, int k) {
        // 构建前缀和
        int [] preSum = new int[nums.length + 1];
        for(int i = 1; i < preSum.length; i++){
            preSum[i] = preSum[i - 1] + nums[i - 1];
        }
        /** 这段注释可以更加直观解题
        nums   [23, 2, 4, 6, 7]
        preSum [0, 23,25,29,35,42]
       	*/
      	// 记忆化(非必须) 
        HashMap<Integer, Integer> map = new HashMap<>(); 
        map.put(0, 1);
      	// 遍历,注意i 的初始值需要根据题意变化
        for(int i =; i < preSum.length; i++){
            
        }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值