携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第21天,点击查看活动详情
题目描述
给你一个整数数组 nums 和一个整数 k ,请你统计并返回 该数组中和为 k 的连续子数组的个数 。
示例 1:
输入: nums = [1,1,1], k = 2
输出: 2
示例 2:
输入: nums = [1,2,3], k = 3
输出: 2
提示:
1 <= nums.length <= 2 * 104-1000 <= nums[i] <= 1000-107 <= k <= 107
解题思路——暴力法
我们初始化一个计数器 count,使用两层循环,外层循环的变量 i 负责控制起始下标,内层循环变量 j 负责游动下标,从 i 的位置开始,变量 j 一直累加到数组结束位置,如果累加结果等于给定的 k,则 count++。
题解
/**
* @param {number[]} nums
* @param {number} k
* @return {number}
*/
var subarraySum = function(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;
};
多少有点惨不忍睹了,但是没关系,我们继续看看有什么别的好方法!
解题思路——前缀和+hashmap
暴力的解法受限于我们每次求 [i, j] 中的和为 k 的子数组时,都要计算一次和,可以用 Map 集合来存储当前遍历元素前的所有元素的和来优化,这么做有什么好处呢?
我们都知道,presum[i] 是 nums[0] + nums[1] + … + nums[i-1] 的和,那么怎么找到和为 k 的子数组呢?当前前缀和 presum[i] 减去前面某个前缀和 presum[n] 的 差值,如果等于 k 的话,是不是就说明被减去的前缀和到当前前缀和之间的元素的和等于 k?是不是有点绕,俺们看个图。
当数组遍历到下标为 4 的元素时,当前前缀和为 12,去寻找前面的前缀和,发现存在一个前缀和为 12-k 的前缀和,说明这个剩下的区间就是和为 k 的数组了。
题解
var subarraySum = function(nums, k) {
let totalSum = 0, count = 0;
const mp = new Map();
mp.set(0, 1);
for(let i=0; i<nums.length; ++i) {
totalSum += nums[i];
if(mp.has(totalSum - k)) {
count += mp.get(totalSum - k);
}
if(mp.has(totalSum)) {
mp.set(totalSum, mp.get(totalSum) + 1);
} else {
mp.set(totalSum, 1);
}
}
return count;
};
结束语
前缀和的思想经常用于求子串和和子数组和的题型上,当我们碰到这类题型,应该想到前缀和的解题方式,好好理解本文,日后对同类型的题型也能轻松应对。