前缀和指一个数组的某下标之前的所有数组元素的和(包含其自身)。前缀和分为一维前缀和,以及二维前缀和。前缀和是一种重要的预处理,能够降低算法的时间复杂度。
一维前缀和
一维前缀和的公式:
sum[i] = sum[i-1] + arr[i]
; sum是前缀和数组, arr是内容数组。拥有前缀和数组后,
我们可以在O(1)的时间复杂度内求出区间和。[i, j]的区间和公式:interval [i, j] = sum[j] - sum[i- 1]
使用前缀和
首先遍历数组构建前缀和数组,在拥有前缀和数组后,通过双层循环计算数组中每一个子项与之前的子项之间的区间和(子数组的和)。
/**
* @param {number[]} nums
* @param {number} k
* @return {number}
*/
var subarraySum = function(nums, k) {
const pre = []
let count = 0
// 构建前缀和数组
for (let i = 0; i < nums.length; i++) {
const a = nums[i]
const b = pre[i - 1] === undefined ? 0 : pre[i - 1]
pre[i] = a + b
}
// 使用前缀和,可以快速获得区间和
for (let i = 0; i < nums.length; i++) {
for (let j = 0; j <= i; j++) {
// 计算区间和,查找到满足条件的区间和,count加一
let intervalSum;
if (j === 0) {
intervalSum = pre[i]
} else if (j === i) {
intervalSum = nums[i]
} else {
intervalSum = pre[i] - pre[j - 1]
}
if (intervalSum === k) {
count += 1
}
}
}
return count
};
前缀和+哈希表
我们之前知道区间和的公式等于
k = sum[j] - sum[i - 1]
, 我们通过简单的移项可以得出这个公式sum[i - 1] = sum[j] - k
。我们在遍历nums时,可以获得当前的前缀和,当前的前缀和减去k,可以得到我们需要找的另一个前缀和的大小,如果hash之中有记录,我们只需要获取hash中的记录,就可以知道有多少区间和等于k了。
/**
* @param {number[]} nums
* @param {number} k
* @return {number}
*/
var subarraySum = function(nums, k) {
let count = 0
let preSum = 0
let hash = {}
for (let i = 0; i < nums.length; i++) {
preSum += nums[i]
const key = preSum - k
if (hash[key]) {
count += hash[key]
}
if (preSum === k) {
count += 1
}
// 记录前缀和出现的次数
if (!hash[preSum]) {
hash[preSum] = 1
} else {
hash[preSum] += 1
}
}
return count
};
作者:dyhtps
链接:https://juejin.cn/post/6944913393627168798