携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第18天,点击查看活动详情
前言
- leetcode hot100,是大厂面试高频题,也是必刷算法题。精选了100道LeetCode上最热门的题目,适合初识算法与数据结构的新手和想要在短时间内高效提升的人,按照官方说的,熟练掌握这 100 道题,就具备了代码世界通行的基本能力。
leetcode560题(和为 K 的子数组)
本文来讲hot100第560题,和为 K 的子数组,本题题目虽然是中等题目,描述也很短,不过通过率是比较低的,很容易用错方法,走入误区。需要仔细分析题目条件。
给你一个整数数组 nums 和一个整数 k ,请你统计并返回 *该数组中和为 k ***的连续子数组的个数 。
示例:
输入: nums = [1,1,1], k = 2
输出: 2
输入: nums = [1,2,3], k = 3
输出: 2
提示(这里的第二个条件很重要):
1 <= nums.length <= 2 * 104-1000 <= nums[i] <= 1000-107 <= k <= 107
分析
- 整数数组,那么有可能每个数是有重复的,每个数也是无序的。
- 和为k的连续子数组,一听到连续子数组,我们就会想到双指针、滑动窗口等方法。
思路
- 如果是
暴力法解决,可以直接使用三层循环,不断的去迭代查找,但是不用想也知道,肯定是超时的,所以这种行不通。 - 忽略暴力法,看到连续子数组,就想直接用
双指针/滑动窗口方法。(错误思路示范)- 想到了数组可能是无序的,所以我们把数组先升序排列,小的放前面,大的放后面
- 设置快慢指针,慢指针在左,快指针在右,然后不断迭代,并累积我们截取的值, 此时会出现三种情况
- 当我们截取的数组之和小于k,说明还不满足题目条件,快指针继续向右执行
- 当我们截取的数组之和等于k,说明满足题目条件,但我们还需要快指针继续右迭代,因为可能存在重复
- 当我们截取的数组之和大于k,说明超出条件,我们慢指针向右走,快指针跟在慢指针右边
- 这是正常的双指针方法,但是最
致命的问题,我们为了解决可能为负值的问题,光想着用排序了,但是忽略了连续子数组这个问题,所以这种思路也不行,我们需要再换种想法。
- 前缀和解法
- 前缀和,从第0项到当前位置的数(nums[i])的和,公式如下
nums[0] + nums[1] + ...+ nums[i]
- 题目要求连续子数组的和为k, 那么和为k的连续子数组的公式如下
nums[0] + nums[1] + ...+ nums[i] = k
- 那么 我们把k移到等号左边,公式如下
nums[0] + nums[1] +...+ nums[i] - k = 0
- 由此可以看出来前缀和与上面的公式结果的前部分一样
[前缀和] - k = 0
- 可以看出,每次只要算出前缀和,我们就可以拿到最终的结果的个数了
- 前缀和,从第0项到当前位置的数(nums[i])的和,公式如下
代码
方式一:先计算前缀和(不算当前的值),再更新map,最后查看和(累加到当前值 - k)相等的i之前的前缀和有多少个
let res = 0;
let map = new Map();
// 保存当前位置的前缀和
let sum = 0;
// 前缀和存储到map中
map.set(0, 1);
for (let i = 0; i < nums.length; i++){
//因为第一个前缀和已处理,所以跳过第一个
if (i !== 0){
sum += nums[i - 1];
map.set(sum, (map.get(sum) || 0) + 1);
}
// 查看和(累加到当前值 - k)相等的i之前的前缀的个数
res += map.get(sum + nums[i] - k)) || 0
}
return res;
方式二:先计算前缀和(算当前的值),再进行统计,最后更新map更新
let res = 0;
let map = new Map();
// 保存当前位置的前缀和
let sum = 0;
// 前缀和存储到map中
map.set(0, 1);
for (let i = 0; i < nums.length; i++) {
sum += nums[i]
if (map.get(sum - k)) {
res += map.get(sum - k)
}
map.set(sum, (map.get(sum) || 0) + 1)
}
return res;
结语
- 本题虽然是中等难度,但是通过率还是比较低的,刚看题目很容易想到双指针/滑动窗口,其实是不适合用的,因为
nums[i]可以小于0,也就是说右指针i向后移1位不能保证区间会增大,左指针j向后移1位也不能保证区间和会减小。给定j,i的位置没有二段性,所以我们需要在这个地方注意一下。 - 本题主要是前缀和的应用,时间复杂度
O(n)空间复杂度O(n)。