前缀和算法(Prefix Sum Algorithm)是一种常用的算法技巧,用于快速计算数组的某些子数组的和。它通过提前计算出数组中元素的累加和,来加速后续的区间和查询,特别适用于需要频繁查询子数组和的场景。
给定一个数组 A
,前缀和数组 S
是通过将 A
中的每个元素累加得到的数组,具体来说,S[i]
存储的是 A[0]
到 A[i]
的和。
前缀和数组的定义:
S[i] = A[0] + A[1] + ... + A[i]
用前缀和数组来求区间和:通过前缀和数组,我们可以非常快速地求出任意区间 [L, R]
的和,公式为:
sum(L, R) = S[R] - S[L-1]
其中 S[R]
是从 A[0]
到 A[R]
的累加和,S[L-1]
是从 A[0]
到 A[L-1]
的累加和,所以 S[R] - S[L-1]
就是 A[L]
到 A[R]
的区间和。
构建前缀和数组:
S
,其中 S[0] = A[0]
。i > 0
,有:S[i] = S[i-1] + A[i]
。查询区间和:
[L, R]
,通过公式 sum(L, R) = S[R] - S[L-1]
计算区间和。n
是数组的长度。示例:
输入:
3 21 2 41 22 3
输出:
36
这个题就是典型的求区间和
代码展示+算法思路
注意:题目l是大于等于1的,所以我们输入的数组是从1开始输入。
#include using namespace std;#include int main() { int n,q; cin>>n>>q; vector arr(n+1); for(int i=1;i<=n;i++) cin>>arr[i]; vector sum(n+1); for(int i=1;i<=n;i++) sum[i]=sum[i-1]+arr[i]; for(int j=0;j>l>>r; cout<
sum
数组是前缀和数组,sum[i]
存储的是从 arr[1]
到 arr[i]
的元素之和。
sum中的元素
被设置为0sum[i] = sum[i - 1] + arr[i]
,也就是每个位置的前缀和是前一个位置的前缀和加上当前元素。[l, r]
,通过前缀和公式 sum[r] - sum[l-1]
来求得区间和。这样,单次查询的时间复杂度为 O(1)。 O(n + q)
,其中 n
是数组的大小, q
是查询的数量。 算法思路分析
填写前缀和矩阵数组的时候,下标直接从 1 开始,能大胆使用 i - 1 , j - 1 位置的值。 注意 dp 表与原数组 matrix 内的元素的映射关系: i. 从 dp 表到 matrix 矩阵,横纵坐标减一; ii. 从 matrix 矩阵到 dp 表,横纵坐标加一。
使用前缀和矩阵
[x1,y1]----[x2,y2]的和
代码展示
#include #include using namespace std;int main() { int n,m,q; int arr[1010][1010]; long long dp[1010][1010]; cin>>n>>m>>q; for(int i=1;i<=n;i++){ for(int j=1;j<=m;j++) cin>>arr[i][j]; } 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<
还是采用的c语言形式定义二维数组比较简便,因为题目给出了行列的数据范围,所以我们直接定义1010为数组行列的个数,初始化都为0 ,不影响加的结果,当然后续遍历也不会用到多余的0数据。
724. 寻找数组的中心下标 - 力扣(LeetCode)
通过读取题意就是求i前后区间的和判断是否相等,返回i即可。
class Solution { public: int pivotIndex(vector& nums) { int n=nums.size(); vector v1(n),v2(n); for(int i=1;i=0;i--) v2[i]=v2[i+1]+nums[i+1]; for(int i=0;i
238. 除自身以外数组的乘积 - 力扣(LeetCode)
class Solution { public: vector productExceptSelf(vector& nums) { vectorv; int n=nums.size(); vectorv1(n,1); vectorv2(n,1); for(int i=1;i=0;i--) v2[i]=v2[i+1]*nums[i+1]; for(int i=0;i
本题其实和上一道题思路很相似,将前后前缀和换成了前后前缀积
560. 和为 K 的子数组 - 力扣(LeetCode)
暴力枚举i区间内所有的子数组
class Solution { public: int subarraySum(vector& nums, int k) { int count = 0; for (int start = 0; start < nums.size(); ++start) { int sum = 0; for (int end = start; end >= 0; --end) { sum += nums[end]; if (sum == k) { count++; } } } return count; }};
但是会重复计算很多次相同数字的和 ,简单优化求出所有前缀和存放在数组中,在暴力枚举所有区间,统计等于k的个数。
class Solution { public: int subarraySum(vector& nums, int k) { int n=nums.size(); vectorv(n+1); for(int i=1;i<=n;i++){ v[i]=v[i-1]+nums[i-1]; } int count=0; for(int i=1;i<=n;i++){ for(int j=0;j
哈希+前缀和,将前缀和存在哈希表中。
class Solution { public: int subarraySum(vector& nums, int k) { unordered_map hash; hash[0]=1; int sum=0; int ret=0; for(auto e: nums ){ sum+=e; if(hash.count(sum-k)) ret+=hash[sum-k]; hash[sum]++; } return ret; }};
本节内容就到此结束了,前缀和的题目还有很多,后续有时间也会继续更新前缀和的相关题目分享,也欢迎友友一起讨论。
最后,感谢各位友友的支持!!!