前置知识
前缀和是一个数组的某下标之前的全部元素之和(包括下标自身)。
前缀和是一种重要的预处理,能够降低算法的时间复杂度。
前缀和的公式:sum[i] = sum[i-1] + arr[i] ; sum是前缀和数组, arr是内容数组。拥有前缀和数组后, 我们可以在O(1)的时间复杂度内求出区间和。
[i, j]的区间和公式:interval [i, j] = sum[j] - sum[i - 1]。
例题
560. 和为 K 的子数组
给定一个整数数组和一个整数 k,你需要找到该数组中和为 k 的连续的子数组的个数。
:::info
输入:nums = [1,1,1], k = 2
输出: 2 , [1,1] 与 [1,1] 为两种不同的情况。
说明 :
数组的长度为 [1, 20,000]。
数组中元素的范围是 [-1000, 1000] ,且整数 k 的范围是 [-1e7, 1e7]。
:::
思路
解法一
先构造一个前缀和,将数组的每个元素的前缀和都记录在一个数组中。
然后二次遍历该数组,计算前缀和之差,当结果与目标和相等时,计数加一。
var subarraySum = function(nums, k) {const len = nums.length;if (!len) return 0;const sums = [];sums[0] = 0;for (let i = 1; i <= len; i++) {sums[i] = sums[i-1] + nums[i-1];}let count = 0;for (let i = 0; i < len; i++) {for (let j = i+1; j < len+1; j++) {let sum = sums[j] - sums[i];if (sum === k) {count++;}}}return count;};
解法二
在解法一中,我们需要O(n2)的时间复杂度以及额外数组,这与直接暴力求解并无太大差异。
为了解决这个问题,我们可以引入哈希表。
首先,我们遍历数组,每经过一个元素,计算前缀和,利用哈希表查找是否存在当前前缀和 - 目标值k。
如果有,则计数增加,将当前前缀和加入到哈希表中。
var subarraySum = function(nums, k) {const len = nums.length;if (!len) return 0;const map = new Map();map.set(0, 1);let pre = 0, count = 0;for (let item of nums) {pre += item;if (map.has(pre - k)) {count += map.get(pre - k)}map.set(pre, (map.get(pre) || 0) + 1);}return count;};
