题目描述
给定一个整数数组和一个整数k,你需要找到该数组中和为k的连续的子数组的个数。
数组的长度为 [1, 20,000]。
数组中元素的范围是 [-1000, 1000] ,且整数k的范围是 [-1e7, 1e7]。
样例
示例 1 :
输入:nums = [1,1,1], k = 2
输出: 2 , [1,1] 与 [1,1] 为两种不同的情况。
说明 :
算法1
(朴素前缀和) O(n^2)
1.构建前缀和数组,以快速计算区间和。
2.固定左端点,调整右端点,使用preSum[right]-preSum[left]计算出区间和是否为k
时间复杂度
参考文献
Java 代码
class Solution {
public int subarraySum(int[] nums, int k) {
int n = nums.length;
// 计算前缀和数组
int[] preSum = new int[n + 1];
for (int i = 0; i < n; i++) {
preSum[i + 1] = preSum[i] + nums[i];
}
// nums = [1,1,1]
int res = 0;
for (int left = 0; left < n; left++) {
for (int right = left; right < n; right++) {
if (preSum[right + 1] - preSum[left] == k) {
res++;
}
}
}
return res;
}
}
算法2
(hash+前缀和) O(n)
1.求一个和为 k 的子数组即为求一对前缀和的差值为 k的下标。
2.用 map 哈希表记录每个前缀和出现的次数。
3.对于当前的前缀和preSum,如果出现preSum - k这个数,说明,有一个区间是满足和为k的连续的子数组的个数,结果也要累加之前 preSum - k 前缀和出现的次数。
参考文献
Java 代码
class Solution {
public int subarraySum(int[] a, int k) {
int len = a.length;
int res = 0;
int sum = 0;
// sum到前缀和的值和出现次数的map
Map<Integer, Integer> map = new HashMap<>();
map.put(0, 1);
for (int i = 0; i < len; i++) {
// sum[0:i]
sum += a[i];
// sum[0:i] - sum[0:j] = k
// sum[0:j] = sum[0:i] - k
if (map.containsKey(sum - k)) {
res += map.get(sum - k);
}
map.put(sum, map.getOrDefault(sum, 0) + 1);
}
return res;
}
}