零基础学算法100天第4天——前缀和(基础算法)

简介: 零基础学算法100天第4天——前缀和(基础算法)

🔥1.背景小故事


          小兔子兔八哥家里有超级多的萝卜坑,每个坑里都有一定的萝卜数量。兔八哥的妈妈为了考验兔八哥,要求每次给它两个坑位x和y,让它在一定时间内告诉自己从x坑到y坑一共有多少个萝卜呢?兔八哥每次都要跑到坑位x数有多少个萝卜,再跑去x+1坑位....直到y,当他兴高采烈的得到答案回家时,妈妈却告诉它——你超时啦!


          为什么会超时呢?


       兔八哥冥思苦想这个问题,每次自己都跑去萝卜坑一个个数,真是太笨了,为什么不拿个本子去记下来呢?兔八哥懊恼的拍了拍脑袋,可是再一想,每次妈妈给定x,y。我还是得用坑x+坑x+1+坑x+2一直到坑y。这太慢了!妥妥的O(n)的时间复杂度。


          直到兔八哥的朋友小黑兔来了以后,兔八哥向他说了自己的烦恼,小黑说这还不简单?于是两人拿了本子去了萝卜坑。先用数组a记录了每个萝卜坑的数量,a[i]表示第i个坑的萝卜数量。然后小黑一阵计算以后给了兔八哥一个数组S,告诉小黑告诉兔八哥每次妈妈告诉你x和y后,用S[y]-S[x-1]就是答案啦。


         兔八哥照做以后,在妈妈念出x和y后下一秒就能给出答案,妥妥的O(1)时间复杂度。而这个神秘的数组S——就是前缀和数组!


🔆 2.什么是前缀和数组?


         其实通过背景小故事,就能很清楚的感受到什么是前缀和数组,以及它的使用场景。它是一种通过对原静态数组预处理得到一个数组,该数组可以用于快速求出原数组的一段区间和。


     原数组a[i]的含义——第i个元素的值


     a[i]的前缀和数组s[i]的含义——数组a中前i个元素的和


    image.png


      如该图,上面的数组为原数组,则下面的数组为改数组对应的前缀和数组。


😾3.如何预处理得到前缀和数组


         首先,我们要知道,前缀和数组s的下标含义s[i]是前i个元素之和。则s[i-1]的含义是前i-1个元素之和,此时我们在s[i-1]的基础上,所以我们可以得知:


        image.png


        但要注意一个问题,一般我们的前缀和数组s[0]一定是等于0的,代表前0个元素之和为0。而且也是为了防止出现下标越界的问题,比如当公式内i等于0时,后面就会出现越界。


       还有一个很重要的问题。


       这个预处理公式并不是一成不变的,这是因为原数组的首位数有可能是下标从0开始,也有可能下标从1开始。当下标从0开始时,原数组第i个元素是a[i-1],当下标从1开始时,原数组第i个元素是a[i]。


       所以在原数组下标为1的情况下,求前缀和的公式也可能是这样:


      image.png


       大家应该灵活变通。


🎃 4.前缀和数组的使用


        我们通过预处理得到了前缀和数组,那么如何使用它呢?


        我们还是通过前缀和的含义出发,假设我们要得到第x个元素到第y个元素之和。而s[y]的值是前y个元素之和,s[x-1]的含义是前n-1个元素之和,两者作差即可得到第x个元素到第y个元素之和。

   公式为:image.png


       这里也体现了为什么前缀和的下标要从1开始,如果从0开始,那么在x-1时又容易产生越界问题。


🍋5.前缀和模板题练习


1)、区域和检索


image.png


题目链接:区域和检索https://leetcode.cn/problems/range-sum-query-immutable/


            实打实的一维前缀和裸题,我们以arr为前缀和数组,先预处理好,然后直接在函数中使用即可,注意这里给定的原数组下标是从0开始。        


class NumArray {
    int[] arr;
    public NumArray(int[] nums) {
        int n=nums.length;
        arr=new int[n+1];
        for(int i=0;i<n;i++){
            arr[i+1]=arr[i]+nums[i];
        }
    }
    public int sumRange(int left, int right) {
        return arr[right+1]-arr[left];
    }
}


2)、连续的子数组和


image.png


题目链接:连续的子数组和https://leetcode.cn/problems/continuous-subarray-sum/                


          题目分析,首先我们要知道同余定理。也就是:


        image.png


           如果我们想找到一段子数组的和是k的倍数,也就说明我们要在前缀和数组中找到两个和为a和b的满足以上式子的关系,同时得保证两者的距离不能小于2。由于是回答是否存在,不需要找出具体的子数组,所以我们可以用set存储前缀和数组中每个元素对k取余的值。


           代码转换:

class Solution {
    public boolean checkSubarraySum(int[] nums, int k) {
        int n=nums.length;
        if(n<2) return false;
        int[] arr=new int[n+1];
        for(int i=0;i<n;++i){
            arr[i+1]=arr[i]+nums[i];
        }
        Set<Integer> set=new HashSet<>();
        for(int i=2;i<n+1;i++){
            set.add(arr[i-2]%k);
            if (set.contains(arr[i]%k)) return true;
        }
        return false;
}
}


3)、连续数组


给定一个二进制数组 nums , 找到含有相同数量的 0 和 1 的最长连续子数组,并返回该子数组的长度。


题目链接:连续数组 https://leetcode.cn/problems/contiguous-array/              


             这道题虽然不是前缀和裸题,却是运用了前缀和的思想,我们知道:数组中只有0和1两个数字,如果我们用变量count记录,遇到0时count--,遇到1时count++。如果一段区间0和1的个数一样,那么count在这两个位置的值就一样,我们用Map来记录,count值为key,下标为value。当遍历到某个位置时,如果前面某个位置的count值和此时位置一样,说明两点区间的0和1个数一样,更新答案的最大值。


class Solution {
    Map<Integer,Integer> map=new HashMap();
    //记录答案
    int res=0;
    int count=0;
    public int findMaxLength(int[] a) {
        int n=a.length;
        map.put(0,-1);
        for(int i=0;i<n;++i){
            if(a[i]==0) --count;
            else count++;
            if(map.containsKey(count)){
                res=Math.max(res,i-map.get(count));
            }else{
                map.put(count,i);
            }
        }
        return res;
    }
}


相关文章
|
4月前
|
算法
【算法】前缀和——二维前缀和模板题
【算法】前缀和——二维前缀和模板题
|
4月前
|
算法
【算法】前缀和——前缀和
【算法】前缀和——前缀和
|
3月前
|
存储 算法 Java
前缀和算法
本文介绍了前缀和及其变种在解决区间求和问题中的应用。首先,一维前缀和可通过预处理数组快速求得任意区间的和。接着,二维前缀和扩展了这一思想,适用于矩阵操作。此外,文章探讨了如何利用前缀和解决诸如“寻找数组中心下标”、“除自身以外数组的乘积”等问题,并进一步讲解了涉及哈希表优化的“和为 K 的子数组”等相关题目。最后,通过实例展示了如何在矩阵中高效计算特定区域的元素之和。文中包含代码示例与图解说明,便于理解。
50 0
前缀和算法
|
2月前
|
人工智能 算法 C++
一篇带你速通前缀和算法(C/C++)
一篇带你速通前缀和算法(C/C++)
|
5月前
|
人工智能 算法 JavaScript
【算法】前缀和与差分
算法学习——前缀和与差分(含一维和二维)
58 4
【算法】前缀和与差分
|
4月前
|
算法 C++
【算法】前缀和算法——和可被K整除的子数组
【算法】前缀和算法——和可被K整除的子数组
|
4月前
|
算法
【算法】前缀和算法——和为k的子数组之和
【算法】前缀和算法——和为k的子数组之和
|
4月前
|
算法
【算法】前缀和——除自身以外数组的乘积
【算法】前缀和——除自身以外数组的乘积
|
4月前
|
算法
【算法】前缀和——寻找数组的中心下标
【算法】前缀和——寻找数组的中心下标
|
7月前
|
算法 C++
c++算法学习笔记 (5)前缀和+差分
c++算法学习笔记 (5)前缀和+差分