题目:
给定一个整数数组和一个整数 k,你需要找到该数组中和为 k 的连续的子数组的个数。
示例 1 :
输入:nums = [1,1,1], k = 2
输出: 2 , [1,1] 与 [1,1] 为两种不同的情况。
方法一:暴力
遇到不会的题目首先要想到一种暴力解法,然后再慢慢优化,本题用暴力解法可以通过,但是也要注意。本题的一种暴力解法显而易见,即可以从元素
i
i
i开始计算
n
u
m
s
[
i
]
nums[i]
nums[i]到
n
u
m
s
[
e
n
d
]
nums[end]
nums[end]的和,如果
n
u
m
s
[
i
]
nums[i]
nums[i]到
n
u
m
s
[
j
]
nums[j]
nums[j]的和恰好等于
k
k
k那么
n
u
m
s
[
i
:
j
]
nums[i:j]
nums[i:j]为一个满足条件的子序列。代码如下,需要十分注意的是size = nums.size()
用size在循环中代替nums.size()
是必须的否则会超时。所以如果采用暴力解法一定要注意代码其他地方的简洁,否则很容易超时。
class Solution {
public:
int subarraySum(vector<int>& nums, int k) {
int size = nums.size(), res = 0;
for (int i = 0; i < size; ++i) {
int sum = 0;
for (int j = i; j < size; ++j) {
sum += nums[j];
res += sum == k ? 1 : 0;
}
}
return res;
}
};
方法二:哈希
考虑如果我们想要一个
O
(
n
)
O(n)
O(n)的算法,那么只能将数组遍历常数次,如果只遍历一次我们只能求得数组的前
i
i
i项和,如果我们假设
p
r
e
[
i
]
pre[i]
pre[i]就表示数组的前
i
i
i项和,那么我们如何表示数组的任意一个子序列
[
i
,
j
]
[i, j]
[i,j]的和呢?稍加思考可以发现
p
r
e
[
i
]
−
p
r
e
[
j
−
1
]
pre[i]-pre[j-1]
pre[i]−pre[j−1]正是子序列
[
i
,
j
]
[i,j]
[i,j]的和。那么要使子序列的和为
k
k
k,即
p
r
e
[
i
]
−
p
r
e
[
j
−
1
]
=
k
pre[i]-pre[j-1] = k
pre[i]−pre[j−1]=k, 通过移项可以得到等价条件
p
r
e
[
i
]
−
k
=
p
r
e
[
j
−
1
]
pre[i] - k = pre[j-1]
pre[i]−k=pre[j−1],意味着我们只要判断在
i
i
i之前的前缀中是否存在一个
p
r
e
[
x
]
pre[x]
pre[x]的值刚好为
p
r
e
[
i
]
−
k
pre[i]-k
pre[i]−k即可。那么也就意味着我们在进行遍历时需要保存每个
p
r
e
[
i
]
pre[i]
pre[i]的值, 同时我们需要统计和出现的次数,所以还要保存每个值对应的出现次数,显而易见这里采用map进行存储是最合适的。代码如下:
class Solution {
public:
int subarraySum(vector<int>& nums, int k) {
unordered_map<int, int> mp;
mp[0] = 1;
int tmp = 0;
int size = nums.size();
int count = 0;
for(int i=0; i<size; ++i)
{
tmp += nums[i];
if(mp.find(tmp-k)!=mp.end()) count += mp[tmp-k];
mp[tmp]++;
}
return count;
}
};