目录
一、前缀和的定义
二、一维前缀和
三、一维前缀和OJ题
3.1、前缀和
3.2、寻找数组中心下标
3.3、除自身以外数组的乘积
3.4、和为K的数组
3.5、和可被K整除的子数组
3.6、连续数组
四、二位前缀和
4.1、二维前缀和
4.2、矩阵区域和
一、前缀和的定义
对于一个给定的数列A,他的前缀和数中 S 中 S[ i ] 表示从第一个元素到第 i 个元素的总和。
如下图:绿色区域的和就是前缀和数组中的 S [ 6 ]。
这里我们需要注意的是:前6个数的和为什么是S【6】呢?数组第6个数下标不应该是5吗?
是的,我们在下表面推导公式会讲到这个问题。
二、一维前缀和
前缀和数组的每一项是可以通过原序列以递推的方式推出来的,递推公式就是:S[ i ] = S[ i - 1 ] + A[ i ]。S[ i - 1 ] 表示前 i - 1 个元素的和,在这基础上加上 A[ i ],就得到了前 i 个元素的和 S [ i ]。
当我们要求的是序列 A 的前 n 个数之和时,如果我们是从下标为 0 的位置开始存储前缀和数组,此公式:sum = S[ r ] - S[ l - 1 ] 显然就无法使用了,为了是这个公式适用于所有情况,我们将从下标为 1 的位置开始存储前缀和,并且将下标为 0 的位置初始化为 0。
三、一维前缀和OJ题
3.1、前缀和
【模板】前缀和_牛客题霸_牛客网 (nowcoder.com)
题目描述:
算法思路:
#include <iostream>
#include<vector>
using namespace std;
int main()
{
int n,q;
cin>>n>>q;
vector<int> arr(n+1,0);
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;
}
3.2、寻找数组中心下标
724. 寻找数组的中心下标 - 力扣(LeetCode)
题目描述:
算法思路:
class Solution {
public:
int pivotIndex(vector<int>& nums)
{
int n=nums.size();
vector<int> f(n),g(n);
//前缀和
for(int i=1;i<n;i++)
f[i]=nums[i-1]+f[i-1];
//后缀和
for(int i=n-2;i>=0;i--)
g[i]=nums[i+1]+g[i+1];
for(int i=0;i<n;i++)
{
if(g[i]==f[i])
return i;
}
return -1;
}
};
3.3、除自身以外数组的乘积
238. 除自身以外数组的乘积 - 力扣(LeetCode)
题目描述:
算法思路:
class Solution {
public:
vector<int> productExceptSelf(vector<int>& nums)
{
int n=nums.size();
vector<int> g(n),f(n);
//前缀积
f[0]=g[n-1]=1;
for(int i=1;i<n;i++)
f[i]=f[i-1]*nums[i-1];
//后缀积
for(int i=n-2;i>=0;i--)
g[i]=g[i+1]*nums[i+1];
vector<int> arr(n);
for(int i=0;i<n;i++)
{
arr[i]=g[i]*f[i];
}
return arr;
}
};
3.4、和为K的数组
560. 和为 K 的子数组 - 力扣(LeetCode)
题目描述:
算法思路:
代码实现:
class Solution {
public:
int subarraySum(vector<int>& nums, int k)
{
unordered_map<int,int> hash;
int sum=0,ret=0;
hash[0]=1;
for(auto x:nums)
{
sum+=x;
if(hash.count(sum-k))
{
ret+=hash[sum-k];
}
hash[sum]++;
}
return ret;
}
};
3.5、和可被K整除的子数组
974. 和可被 K 整除的子数组 - 力扣(LeetCode)
题目描述:
算法思路:
class Solution {
public:
int subarraysDivByK(vector<int>& nums, int k)
{
unordered_map<int,int> hash; //第一个int存余数,第二个存个数
int sum=0,ret=0;
hash[0%k]=1;
for(auto x:nums)
{
sum+=x;
int r=(sum%k+k)%k;
if(hash.count(r)) ret+=hash[r];
hash[r]++;
}
return ret;
}
};
3.6、连续数组
525. 连续数组 - 力扣(LeetCode)
题目描述:
class Solution {
public:
int findMaxLength(vector<int>& nums)
{
unordered_map<int,int> hash;
hash[0]=-1;
int sum=0,ret=0;
for(int i=0;i<nums.size();i++)
{
sum+=nums[i]==0?-1:1;//当前位置的前缀和,将0变-1
if(hash.count(sum)) ret=max(ret,i-hash[sum]);
else hash[sum]=i;
}
return ret;
}
};
四、二位前缀和
例如:对与 x = 4,y = 3 这么一组输入,就是将原矩阵序列中蓝色区域的元素相加,得到的结果便是前缀和矩阵S中 S[ 4 ][ 3 ] 的值。
例如上图:我们要求蓝色矩阵中所有元素的和。
现在就差最后一步了,怎么求出前缀和矩阵中的每一个值嘞??同理利用递推关系求就阔以啦。
S[ i ][ j ] = S[ i - 1 ][ j ] + S[ i ][ j - 1 ] - S[ i - 1][ j - 1 ] + a[ i ][ j ]
五、二维前缀和OJ题
4.1、二维前缀和
【模板】二维前缀和_牛客题霸_牛客网 (nowcoder.com)
题目描述:
算法思路:
- 首先对矩阵进行预处理,得到对应的前缀和矩阵。
- 利用前缀和矩阵相应区域的加减运算,即可得到对应子矩阵中所有元素的累加和。
图解展示(图中presum[3][4]除了包括绿色部分,还包括其它重叠的部分,其它几项也一样,另外presum[1][1]被多减了一次,所以最后要加一次):
代码实现:
#include <iostream>
#include<vector>
using namespace std;
int main()
{
int n,m,q;
cin>>n>>m>>q;
vector<vector<long long>> arr(n+1,vector<long long>(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,x2,y1,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;
}
return 0;
}
4.2、矩阵区域和
1314. 矩阵区域和 - 力扣(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] - dp[i - 1][j - 1] + mat[i - 1][j - 1];
}
}
//使用前缀和矩阵
vector<vector<int>> ret(m,vector<int>(n));
for(int i=0;i<m;i++)
{
for(int j=0;j<n;j++)
{
int x1 = max(0, i - k) + 1, y1 = max(0, j - k) + 1;
int x2 = min(m - 1, i + k) + 1, y2 = min(n - 1, j + k) + 1;
ret[i][j] = dp[x2][y2] - dp[x1 - 1][y2] - dp[x2][y1 - 1] + dp[x1 - 1][y1 - 1];
}
}
return ret;
}
};