算法思想总结:前缀和算法

简介: 算法思想总结:前缀和算法

                                                   创作不易,感谢三连 !!

一、【模版】前缀和

牛客网—【模版】前缀和

#include<iostream>
#include<vector>
using namespace std;
int main() 
{
  int n,q;
  cin>>n>>q;
  //创建数组 下标从1开始
  vector<int> arr(n+1);
  for(int i=1;i<=n;++i)  cin>>arr[i];   
  //创建一个前缀和数组
  vector<long long> dp(n+1);//默认初始化的时候是0
  for(int i=1;i<=n;++i)  dp[i]=arr[i]+dp[i-1];
  //使用前缀和数组
  int l,r;
  while(q--)
  {
    cin>>l>>r;
    cout<<dp[r]-dp[l-1]<<endl;
  }
  return 0;
}

二、【模板】二维前缀和

牛客网—【模版】二维前缀和

#include <iostream>
#include <vector>
using namespace std;
int main() 
{
   int n,m,q;
   cin>>n>>m>>q;
   //根据m和n创建二维数组 
   vector<vector<int>> arr(n+1,vector<int>(m+1));
   for(int i=1;i<=n;++i) for(int j=1;j<=m;++j) cin>>arr[i][j];
   //创建前缀和矩阵
    vector<vector<long long>> dp(n+1,vector<long long>(m+1));//防止溢出
   for(int i=1;i<=n;++i) for(int j=1;j<=m;++j) 
      dp[i][j]=dp[i-1][j]+dp[i][j-1]+arr[i][j]-dp[i-1][j-1];
   //使用前缀和矩阵
   int x1,y1,x2,y2;
   while(q--)
   {
     cin>>x1>>y1>>x2>>y2;
     cout<<dp[x2][y2]-dp[x1-1][y2]-dp[x2][y1-1]+dp[x1-1][y1-1]<<endl;
   }
}

三、寻找数组的中间下标

. - 力扣(LeetCode)寻找数组的中间下标

class Solution {
public:
    int pivotIndex(vector<int>& nums) 
    {
        int n=nums.size();
         //创建一个前缀和数组
         vector<int>  dpq(n);
         for(int i=1;i<n;++i)  dpq[i]=nums[i-1]+dpq[i-1]; 
         //创建一个后缀和数组
         vector<int>  dph(n);  
         for(int i=n-2;i>=0;--i)  dph[i]=nums[i+1]+dph[i+1]; 
         //使用前缀和和后缀和数组
         for(int i=0;i<n;++i)  if(dpq[i]==dph[i])  return i;
         return -1;
    }
};

四、除自身以外数组的乘积

. - 力扣(LeetCode)除自身以外数组的乘积

class Solution {
public:
    vector<int> productExceptSelf(vector<int>& nums)
    {
       int n=nums.size();//记录有效个数
       //创建一个前缀乘积数组
       vector<int> dpq(n,1);
       for(int i=1;i<n;++i) dpq[i]=nums[i-1]*dpq[i-1];
       //创建一个后缀乘积数组
       vector<int> dph(n,1); 
       for(int i=n-2;i>=0;--i)  dph[i]=nums[i+1]*dph[i+1];
       //使用这两个数组并创建answer
       vector<int> answer(n);
       for(int i=0;i<n;++i) answer[i]=dpq[i]*dph[i];
       return answer;
    } 
};

五、 和为k的子数组

. - 力扣(LeetCode)

class Solution {
public:
    int subarraySum(vector<int>& nums, int k) 
    {
         unordered_map<int,int> hash;//用来统计前缀和的个数
         hash[0]=1;//默认有一个前缀和为0的数组
         int sum=0;//记录前缀和
         int ret=0;//记录有多少个和为k的子数组
         for(auto e:nums)
         {
            sum+=e;
            if(hash.count(sum-k))  ret+=hash[sum-k];
            ++hash[sum];
         }
         return ret;
    }
};

六、和可被k整除的子数组

. - 力扣(LeetCode)

class Solution {
public:
    int subarraysDivByK(vector<int>& nums, int k) 
    {
      unordered_map<int,int> hash;//用来统计前缀和%k的余数
      hash[0]=1;
      int sum=0;//统计前缀和
      int ret=0;//统计子数的个数
      for(auto e:nums) 
      {
          sum+=e;
          int temp=(sum%k+k)%k;//确保temp为正数
          if(hash[temp]) ret+=hash[temp];//用ret去统计符合要求的子数组个数
          ++hash[temp];//将该sum对应的余数继续存进去
      }
      return ret;
     }
};

七、连续数组

. - 力扣(LeetCode)

class Solution {
public:
    //将数组中为0的改成-1.找到一个最长子数组 其元素和结果恰好为0
    int findMaxLength(vector<int>& nums) 
    {
        unordered_map<int,int> hash;//用来统计前缀和的下标
        hash[0]=-1;//[0,x-1]之间是否有前缀和是sum,如果x=0,那么x-1=-1,也解释了为什么hash[0]=-1
        int sum=0;
        int ret=0;//用来更新最长的长度
        for(int i=0;i<nums.size();++i)  
        {
           sum+=nums[i]==0?-1:1;
           if(hash.count(sum)) ret=max(ret,i-hash[sum]);
           else hash[sum]=i;//只需要存储最早之前的下标就可以了,因为我们要找的是最长的
        }
        return ret;
    }
};

八、最大子数组的和

. - 力扣(LeetCode)

class Solution {
public:
    int maxSubArray(vector<int>& nums) 
    {
       int sum=0,Max=INT_MIN,Min=0;
       for(auto e:nums) 
        {
            sum+=e;
            Max=max(Max,sum-Min);
            Min=min(Min,sum);
        }
        return Max;
    }
};

九、矩阵区域和

. - 力扣(LeetCode)

class Solution {
public:
    vector<vector<int>> matrixBlockSum(vector<vector<int>>& mat, int k) 
    {
        int m=mat.size(),n=mat[0].size();
       //根据矩阵创建一个前缀和数组
       vector<vector<int>> dp(m+1,vector<int>(n+1));
       //开始对矩阵进行值的输入
       for(int i=1;i<=m;++i)
         for(int j=1;j<=n;++j)
            dp[i][j]=dp[i-1][j]+dp[i][j-1]+mat[i-1][j-1]-dp[i-1][j-1];
        //开始使用前缀和数组 输入返回的数组
        vector<vector<int>> answer(m,vector<int>(n));//需要返回的数组
         for(int i=0;i<m;++i)
           for(int j=0;j<n;++j)
             {
                int x1=max(i-k,0)+1,y1=max(j-k,0)+1;
                int x2=min(i+k,m-1)+1,y2=min(j+k,n-1)+1;
               answer[i][j]=dp[x2][y2]+dp[x1-1][y1-1]-dp[x2][y1-1]-dp[x1-1][y2];
             }
             return answer;
    }
};

十、前缀和算法总结

1、前缀和并不一定真的需要搞一个前缀和数组

2、用哈希存前缀和相关数据的话要注意hash[0]

3、无论dp数组是开的n还是开的n+1,都要注意和原数组之间的映射关系

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