
前缀和是一种用于快速计算数组或矩阵中某个连续区间或子矩阵元素之和的数据预处理技术。其核心思想是预先计算并存储从起始位置到每个位置的所有元素之和从而将后续的区间求和查询时间复杂度从 O(n) 或 O(n²) 降低到 O(1) 。一维前缀和对于长度为n的数组a其前缀和数组s定义为s[i] a[0] a[1] ... a[i-1]通常s[0] 0以便统一处理从索引0 开始的区间。这样原数组a中区间[l, r]闭区间的和可以通过s[r1] - s[l]在 O(1) 时间内得到 。构建与查询示例#include vector #include iostream using namespace std; int main() { vectorint a {1, 2, 3, 4, 5}; int n a.size(); vectorint s(n 1, 0); // 前缀和数组长度 n1s[0]0 // 构建前缀和数组 for (int i 1; i n; i) { s[i] s[i - 1] a[i1]; // s[i] 表示 a[0] 到 a[i-1] 的和 } // 查询区间 [l, r] 的和 (0-indexed) int l 1, r 3; // 对应原数组元素 2, 3, 4 int sum s[r 1] - s[l]; // s[4] - s[1] 10 - 1 9 cout Sum of a[ l ... r ] sum endl; // 输出 9 return 0; }二维前缀和对于m x n的二维矩阵a其二维前缀和数组s定义为s[i][j]表示所有a[x][y]其中0 x i,0 y j的和。子矩阵(x1, y1)到(x2, y2)左上角和右下角坐标的和可以通过容斥原理计算sum s[x21][y21] - s[x1][y21] - s[x21][y1] s[x1][y1]。构建与查询示例#include vector #include iostream using namespace std; int main() { vectorvectorint a {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}}; int m a.size(), n a[0].size(); vectorvectorint s(m 1, vectorint(n 1, 0)); // 构建二维前缀和数组 for (int i 1; i m; i) { for (int j 1; j n; j) { // 容斥原理当前和 上方和 左方和 - 左上和 当前元素值 s[i][j] s[i-1][j] s[i][j-1] - s[i-1][j-1] a[i-1][j-1]; } } // 查询子矩阵 (x1, y1) 到 (x2, y2) 的和 (0-indexed) int x1 0, y1 0, x2 1, y2 1; // 对应元素1,2,4,5 int sum_rect s[x21][y21] - s[x1][y21] - s[x21][y1] s[x1][y1]; cout Sum of submatrix sum_rect endl; // 输出 12 return 0; }核心应用与对比维度预处理时间复杂度单次查询时间复杂度核心公式区间/子矩阵和典型应用场景一维前缀和O(n)O(1)sum[l, r] s[r1] - s[l]连续子数组和问题、区间求和统计二维前缀和O(m*n)O(1)sum s[x21][y21] - s[x1][y21] - s[x21][y1] s[x1][y1]图像处理、子矩阵和计算、动态规划优化高维前缀和O(k * n^k) (k为维度)O(1)基于多维容斥原理子集/超集求和、高维统计问题典型问题示例寻找数组的中心下标LeetCode 724中心下标左侧所有元素和等于右侧所有元素和。使用前缀和可高效求解 。int total accumulate(nums.begin(), nums.end(), 0); int leftSum 0; for (int i 0; i nums.size(); i) { if (leftSum total - leftSum - nums[i]) return i; leftSum nums[i]; } return -1; }除自身以外数组的乘积LeetCode 238可构造前缀积和后缀积数组使时间复杂度为 O(n)空间复杂度为 O(1)输出数组不计。和为 K 的子数组LeetCode 560利用前缀和与哈希表可在 O(n) 时间内统计和为 k 的连续子数组个数 。int subarraySum(vectorint nums, int k) { unordered_mapint, int prefixSumCount; // 存储前缀和出现的次数 prefixSumCount[0] 1; // 初始前缀和为0出现1次 int sum 0, count 0; for (int num : nums) { sum num; // 当前前缀和 // 如果存在前缀和 (sum - k)则说明中间有一段子数组和为 k if (prefixSumCount.find(sum - k) ! prefixSumCount.end()) { count prefixSumCount[sum - k]; } prefixSumCount[sum]; } return count; }使用步骤与注意事项预处理根据原数组构造前缀和数组注意下标偏移通常前缀和数组比原数组长度多1且s[0] 0。查询利用前缀和数组的差分性质进行 O(1) 查询。边界处理特别注意数组下标范围防止越界访问。空间优化某些问题如“除自身以外数组的乘积”可以通过变量滚动替代显式的前缀和数组将空间复杂度优化到 O(1) 。参考来源【基础算法总结】前缀和前缀和——2二维数组前缀和前缀和——1什么是前缀和和一维前缀和C前缀和高维前缀和总结