前言
前缀和(Prefix Sum)是一种简单却极其强大的算法技巧,它能在许多问题中显著降低时间复杂度。本文将详细介绍前缀和的概念、实现方法,并通过LeetCode例题展示其应用场景。
一、什么是前缀和?
前缀和,又称累计和,是一种预处理技术,它通过预先计算并存储数组的累计和,使得后续的区间求和查询可以在常数时间内完成。
简单来说,对于一个数组 nums
,我们构造一个前缀和数组 prefixSum
,其中:
prefixSum[i] = nums[0] + nums[1] + ... + nums[i-1]
或者另一种常见定义:
prefixSum[i] = nums[0] + nums[1] + ... + nums[i]
二、前缀和的基本实现
让我们看看如何在JavaScript中实现前缀和:
// 标准前缀和实现
function buildPrefixSum(nums) {
const prefixSum = new Array(nums.length + 1).fill(0);
for (let i = 0; i < nums.length; i++) {
prefixSum[i + 1] = prefixSum[i] + nums[i];
}
return prefixSum;
}
// 使用示例
const nums = [1, 2, 3, 4, 5];
const prefixSum = buildPrefixSum(nums);
console.log(prefixSum); // [0, 1, 3, 6, 10, 15]
这种实现方式下,计算区间 [i, j]
的和可以表示为:
const rangeSum = prefixSum[j + 1] - prefixSum[i];
注意:我们一定要搞清楚前缀和的区间
prefixSum[i]
代表着是[0 , i]
的和还是[0 , i - 1]
的和,这要看自己的前缀和是怎么实现的,一定要搞清楚
三、LeetCode例题解析
例题1:560. 和为 K 的子数组 - 力扣(LeetCode)
题目描述:给定一个整数数组和一个整数k,你需要找到该数组中和为k的连续子数组的个数。
暴力解法(O(n²)):
function subarraySum(nums, k) {
let count = 0;
for (let start = 0; start < nums.length; start++) {
let sum = 0;
for (let end = start; end < nums.length; end++) {
sum += nums[end];
if (sum === k) {
count++;
}
}
}
return count;
}
前缀和优化解法(O(n)):
function subarraySum(nums, k) {
const map = new Map();
map.set(0, 1); // 初始状态,和为0出现1次
let prefixSum = 0;
let count = 0;
for (const num of nums) {
prefixSum += num;
if (map.has(prefixSum - k)) {
count += map.get(prefixSum - k);
}
map.set(prefixSum, (map.get(prefixSum) || 0) + 1);
}
return count;
}
例题2: 304. 二维区域和检索 - 矩阵不可变 - 力扣(LeetCode)
题目描述:给定一个二维矩阵,计算其子矩形范围内元素的总和。
前缀和解法:
class NumMatrix {
constructor(matrix) {
if (!matrix.length || !matrix[0].length) {
this.prefix = [[]];
return;
}
const rows = matrix.length;
const cols = matrix[0].length;
this.prefix = Array.from({ length: rows + 1 }, () => new Array(cols + 1).fill(0));
for (let i = 1; i <= rows; i++) {
for (let j = 1; j <= cols; j++) {
this.prefix[i][j] = matrix[i-1][j-1]
+ this.prefix[i-1][j]
+ this.prefix[i][j-1]
- this.prefix[i-1][j-1];
}
}
}
sumRegion(row1, col1, row2, col2) {
return this.prefix[row2+1][col2+1]
- this.prefix[row1][col2+1]
- this.prefix[row2+1][col1]
+ this.prefix[row1][col1];
}
}
四、总结
前缀和是一种简单却强大的预处理技术,它通过空间换时间的方式显著提高了区间求和操作的效率。掌握前缀和不仅可以帮助我们解决许多LeetCode中的中等难度问题,还能为解决更复杂的算法问题奠定基础。
适合场景:
- 需要频繁查询区间和
- 数据不经常变动(否则需要更高级的数据结构如线段树)
在实际应用中,前缀和常常与其他技巧如哈希表、滑动窗口等结合使用,形成更高效的解决方案。理解前缀和的本质并能灵活运用,是算法学习中的一个重要里程碑。