1. 前缀和思想
前缀和是指从数组第一个元素开始,累加到当前位置得到的累计和序列。是典型的空间换时间思想。
以一维前缀和数组为例:

2. 例题分析
2.1 一维前缀和
解题思路:
假设一共查询Q次:
- 暴力+模拟
O(N * Q):每次查询时,计算出给定的区间和并返回。
由于查询次数可能会很大,暴力解法的时间复杂度会随着Q的增大而倍增。因此可以空间换时间 ,使用前缀和思想。
- 前缀和
时间O(N + Q),空间O(N):新建一个前缀和数组,每次查询时直接返回区间和 (两个前缀和之差)。
遍历数组计算前缀和,需要O(N)的时间;查询Q次,每次计算消耗O(1)的时间。因此总的时间复杂度为O(N + Q),空间复杂度为O(N)。

cpp
#include<iostream>
#include<vector>
using namespace std;
int main()
{
int n, t; cin >> n >> t;
vector<long long> v(n + 1, 0);
for(int i=1; i<=n; i++)
{
int x; cin >> x;
v[i] = v[i - 1] + x;
}
while(t--)
{
int l, r; cin >> l >> r;
cout << v[r] - v[l - 1] << endl;
}
return 0;
}
2.2 二维前缀和
解题思路:
类比于一维前缀和数组的形式,我们可以试着处理从[0, 0]位置到[i, j]位置这片区域内所有元素的累加和,这样就可以在O(1)的时间内计算出矩阵内任意区域的元素累加和。
- 二维前缀和
时间O(N^2 + Q),空间O(N^2):读取数组元素,据此新创建一个二维前缀和数组,最后根据输入的坐标计算区域的累加和即可。
在创建二维前缀和数组时,为了方便写代码和避免越界访问,我们一般会把第一行第一列 全部设置为0,从dp[1][1]开始填入数据。对应关系是行列相差1:
和一维前缀和相同的策略,我们也是采用一边遍历,一边计算前缀和的方式填数。下图演示了前缀和数组的填数和使用 :

cpp
#include<iostream>
#include<vector>
using namespace std;
int main() {
int n, m, q;
cin >> n >> m >> q;
vector<vector<long long>> vv(n + 1, vector<long long>(m + 1, 0));
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m; j++) {
cin >> vv[i][j];
}
}
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m; j++) {
vv[i][j] += vv[i - 1][j] + vv[i][j - 1] - vv[i - 1][j - 1];
}
}
while (q--) {
int x1, x2, y1, y2;
cin >> x1 >> y1 >> x2 >> y2;
long long ret = vv[x2][y2] - vv[x1 - 1][y2] - vv[x2][y1 - 1] + vv[x1 - 1][y1 -
1];
cout << ret << endl;
}
return 0;
}
2. 3 寻找数组的中心下标
解题思路:
我们发现对于每一个下标都要计算其左侧和右侧的区间和,因此采用前缀和的解法优化时间复杂度:
- 前缀和
时间O(N),空间O(N):预处理出一个前缀和数组和一个后缀和数组,遍历下标,从这两个数组中找出左右区间和相等的下标,若没有则返回-1。
注意点:
- 前缀和数组
fx中,fx[i]表示[0, i - 1]区间所有元素的和 - 后缀和数组
gx中,gx[i]表示[i + 1, n - 1]区间所有元素的和
cpp
class Solution {
public:
int pivotIndex(vector<int>& nums)
{
int n = nums.size();
vector<int> fx(n, 0);
vector<int> gx(n, 0);
fx[0] = nums[0];
gx[n - 1] = nums[n - 1];
for(int i=1; i<n; i++)
{
fx[i] = fx[i - 1] + nums[i];
}
for(int i=n-2; i>=0; i--)
{
gx[i] = gx[i + 1] + nums[i];
}
for(int i=0; i<n; i++)
{
int left = (i == 0 ? 0 : fx[i - 1]);
int right = (i == n - 1 ? 0 : gx[i + 1]);
if(left == right)
{
return i;
}
}
return -1;
}
};
// 本期内容就到这里啦,如果对你有帮助,请三连支持!我是青云,我们下期见^_~