目录
🔥 前缀和核心价值
暴力法的痛点:
// 计算区间和,时间复杂度O(n) int sum = 0; for(int i=l; i<=r; i++) sum += arr[i];
前缀和优势:将区间查询复杂度从 O(n) 降为 O(1),处理海量数据时性能炸裂!
🌟 一维前缀和模板
1. 预处理公式
prefix[i] = prefix[i-1] + arr[i]
区间和公式:sum(l, r) = prefix[r] - prefix[l-1]
2. 代码实现
vector<int> arr = {3, 1, 4, 2, 5};
int n = arr.size();
// 前缀和数组(从1开始存储)
vector<int> prefix(n+1, 0);
for(int i=1; i<=n; i++) {
prefix[i] = prefix[i-1] + arr[i-1]; // arr索引注意-1
}
// 查询区间[2,4]的和(对应原数组索引1~3)
int l=2, r=4;
int sum = prefix[r] - prefix[l-1]; // 4+2+5=11
3. 动态图示
原数组索引:0 1 2 3 4
原数组值: 3 1 4 2 5
前缀和数组:0 3 4 8 10 15
查询[2,4] → prefix[4] - prefix[1] = 10 - 3 = 7 ❌
正确对应:原数组索引1~3 → 前缀和[2,4] → 10 - 3 = 7
(注意索引转换的细节!)
📦 二维前缀和模板
1. 预处理公式
prefix[i][j] = prefix[i-1][j] + prefix[i][j-1] - prefix[i-1][j-1] + matrix[i-1][j-1]
区间和公式:sum(x1,y1,x2,y2) = prefix[x2][y2] - prefix[x1-1][y2] - prefix[x2][y1-1] + prefix[x1-1][y1-1]
2. 代码实现
vector<vector<int>> matrix = {{1,2,3}, {4,5,6}, {7,8,9}};
int m = matrix.size(), n = matrix[0].size();
vector<vector<int>> prefix(m+1, vector<int>(n+1, 0));
for(int i=1; i<=m; i++){
for(int j=1; j<=n; j++){
prefix[i][j] = prefix[i-1][j] + prefix[i][j-1] - prefix[i-1][j-1] + matrix[i-1][j-1];
}
}
// 查询子矩阵和 (x1,y1)=(2,2) 到 (x2,y2)=(3,3)
int sum = prefix[3][3] - prefix[1][3] - prefix[3][1] + prefix[1][1];
// 对应原矩阵的右下角4个元素:5+6+8+9=28
3. 二维示意图
原矩阵:
1 2 3
4 5 6
7 8 9
前缀和矩阵(带虚拟边界):
0 0 0 0
0 1 3 6
0 5 12 21
0 12 27 45
查询(2,2)-(3,3)区域:
prefix[3][3] - prefix[1][3] - prefix[3][1] + prefix[1][1]
=45 - 6 - 12 + 1 = 28
🚨 六大避坑指南
-
索引偏移陷阱:前缀和数组通常从1开始,原数组从0开始
-
初始化边界:
prefix[0] = 0
-
整数溢出:用
long long
存储前缀和 -
二维减法的容斥原理:注意加减顺序
-
空区间处理:当l>r时应返回0
-
负数处理:哈希表统计时需考虑负前缀和
💡 复杂度分析
操作 | 一维 | 二维 |
---|---|---|
预处理 | O(n) | O(mn) |
单次查询 | O(1) | O(1) |
空间复杂度 | O(n) | O(mn) |
🌈 LeetCode实战
-
303. 区域和检索 - 数组不可变(一维模板题)
-
304. 二维区域和检索 - 矩阵不可变(二维模板题)
-
560. 和为K的子数组(哈希表+前缀和经典)
-
1248. 统计「优美子数组」(奇偶性转换技巧)