和(积)为k的子数组
和为k的子数组
1. 问题描述
给定一个整数数组和一个整数 k,你需要找到该数组中和为 k 的连续的子数组的个数。
2.解题思路一
- 暴力破解,双重for循环
- 若和等于k,计数+1
var subarraySum = function(nums, k) {
var count = 0;
for(var i = 0; i < nums.length; i++){
let sum = 0;
for(var j = i; j < nums.length; j++){
sum += nums[j];
if(sum == k) count++;
}
}
return count;
};
3. 图解一
输入:nums = [1,7,8,-2], k = 8
输出: 2 , [1,7] 与 [8] 为两种。
ij | sum | k | 执行 |
---|---|---|---|
00 | 0+1 | 8 | j++ |
01 | 0+1+7 | 8 | count++,j++ |
02 | 0+1+7+8 | 8 | j++ |
03 | 0+1+7+8-2 | 8 | j++ |
04 | 8 | i++ | |
11 | 0+7 | 8 | j++ |
12 | 0+7+8 | 8 | j++ |
13 | 0+7+8-2 | 8 | j++ |
14 | 8 | i++ | |
22 | 0+8 | 8 | count++,j++ |
23 | 0+8-2 | 8 | j++ |
24 | 8 | i++ | |
33 | 0-2 | 8 | j++ |
34 | 8 | i++ | |
44 | 8 | return count |
4. 解题思路二
- 哈希表存储,前缀和,以及出现的次数
- 若在哈希表里找到前缀和-k的值,计数+1
- 前缀和出现在哈希表里,出现次数+1
- 前缀和不在哈希表里,设置哈希表
var subarraySum = function(nums, k) {
var mp = new Map();
mp.set(0,1);
var pre = 0;
var count = 0;
for(const num of nums){
pre += num;
if(mp.has(pre - k)) count += mp.get(pre - k);
if(mp.has(pre)) mp.set(pre, mp.get(pre) + 1);
else mp.set(pre, 1);
}
return count;
};
5. 图解二
输入:nums = [1,7,8,-2], k = 8
输出: 2 , [1,7] 与 [8] 为两种。
步骤 | 初始 | 1 | 2 | 3 | 4 |
---|---|---|---|---|---|
num | 1 | 7 | 8 | -2 | |
pre | 0 | 0+1 | 0+1+7 | 0+1+7+8 | 0+1+7+8-2 |
pre-k | 0 | 1-8 | 8-8 | 16-8 | 14-8 |
count | 0 | 1 | 2 | ||
mp | (0,1) | [(0,1),(1,1)] | [(0,1),(1,1),(8,1) | [(0,1),(1,1),(8,1),(16,1)] | [(0,1),(1,1),(8,1),(16,1),(14,1)] |
连续的子数组和
1. 问题描述
给定一个包含 非负数 的数组和一个目标 整数 k ,编写一个函数来判断该数组是否含有连续的子数组,其大小至少为 2,且总和为 k 的倍数,即总和为 n * k ,其中 n 也是一个整数。
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/continuous-subarray-sum
2. 解题思路
- 创建哈希表,存储前缀和mod k的余和下标
- 将前缀和mod k,取余
- 若下标值出现在哈希表中,说明取到子数组和为k或k的倍数
- 当前遍历值-下标值,若大于1,说明子数组大小至少为2
var checkSubarraySum = function(nums, k) {
var mp = new Map();
mp.set(0, -1);//记录(前缀和,下标)
var pre = 0;
for(var i = 0; i < nums.length; i++){
pre += nums[i];
//前缀和取余,循环之后再次出现,表示取到了总和为k倍数的子数组
const nk = k == 0 ? pre : pre % k;
if(mp.has(nk)){
//下标差值大于1,表示子数组大小至少为2
if((i - mp.get(nk)) > 1) return true;
}
else mp.set(nk, i);
}
return false;
};
3. 图解
输入:[23,2,4,6,7], k = 6
输出:True
解释:[2,4] 是一个大小为 2 的子数组,并且和为 6。
步骤 | 初始 | 1 | 2 | 3 |
---|---|---|---|---|
i | 0 | 1 | 2 | |
num[i] | 23 | 2 | 4 | |
pre | 0 | 0+23 | 0+23+2 | 0+23+2+4 |
nk | 0 | 5 | 1 | 5 |
mp | (0,-1) | [(0,-1),(5,0)] | [(0,-1),(5,0),(1,1)] | [(0,-1),(5,0),(1,1)] |
i-mp.get(nk) | 2 - 0 > 1 |
乘积小于k的子数组
1. 问题描述
给定一个正整数数组 nums。
找出该数组内乘积小于 k 的连续的子数组的个数。
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/subarray-product-less-than-k
2. 解题思路一
- 双重for循环
- 乘积小于k,计数+1
var numSubarrayProductLessThanK = function(nums, k) {
var count = 0;
for(var i = 0; i < nums.length; i++){
var pre = 1;
for(var j = i; j < nums.length; j++){
pre *= nums[j];
if(pre < k) count++;
}
}
return count;
};
3. 图解一
输入: nums = [10,5,2,6], k = 100
输出: 8
解释: 8个乘积小于100的子数组分别为: [10], [5], [2], [6], [10,5], [5,2], [2,6], [5,2,6]。
需要注意的是 [10,5,2] 并不是乘积小于100的子数组。
轮数(ij) | num[j] | pre | k | count | 执行 |
---|---|---|---|---|---|
00 | 10 | 1*10 | 100 | 0 | pre<k,count++,j++ |
01 | 5 | 1*10*5 | 100 | 1 | pre<k,count++,j++ |
02 | 2 | 1*10*5*2 | 100 | 2 | pre=k,j++ |
03 | 6 | 1*10*5*2*6 | 100 | 2 | pre>k,j++ |
04 | 100 | 2 | i++ | ||
11 | 5 | 1*5 | 100 | 2 | pre<k,count++,j++ |
12 | 2 | 1*5*2 | 100 | 3 | pre<k,count++,j++ |
13 | 6 | 1*5*2*6 | 100 | 4 | pre<k,count++,j++ |
14 | 100 | 5 | i++ | ||
22 | 2 | 1*2 | 100 | 5 | pre<k,count++,j++ |
23 | 6 | 1*2*6 | 100 | 6 | pre<k,count++,j++ |
24 | 100 | 7 | i++ | ||
33 | 6 | 1*6 | 100 | 7 | pre<k,count++,j++ |
34 | 100 | 8 | i++ |
2. 解题思路二
- 从数组左边开始滑动
- 依次向右进行乘积,若遇到乘积大于等于k,左指针就向右移
var numSubarrayProductLessThanK = function(nums, k) {
var count = 0;
var pre = 1;
var left = 0;
if(k <= 1) return 0;
for(right = 0; right < nums.length; right++){
pre *= nums[right];
while(pre >= k){
pre /= nums[left];
left++;
}
count += right - left + 1;
}
return count;
};
3. 图解二
输入: nums = [10,5,2,6], k = 100
输出: 8
解释: 8个乘积小于100的子数组分别为: [10], [5], [2], [6], [10,5], [5,2], [2,6], [5,2,6]。
需要注意的是 [10,5,2] 并不是乘积小于100的子数组。
0 | 1 | 2 | 3 | 执行 | |
---|---|---|---|---|---|
nums | 10 | 5 | 2 | 6 | |
right/left | count=0-0+1=1,right++ | ||||
left | right | count=1+ 1-0+1=3,right++ | |||
left | right | pre=k,pre/num[left],left++ | |||
left | right | count=3 + 2-1+1=5,right++ | |||
left | right | count=5 + 3-1+1=8,right++ |