20.<tag-数组和前缀和(并用哈希表优化)> lt.560-和为 K 的子数组 0.8
lt.560-和为 K 的子数组
[案例需求]

[思路分析一, 暴力解法: 双重for循环(固定一个边界)]
固定了起点,即先固定左边界,然后枚举右边界.
- 外层循环表示start不断的靠近数组末尾,
- 内层循环表示不断从end到0倒着连加, 把每次==k的数, 在计数器count上+1;
[代码实现]
public class Solution {
public int subarraySum(int[] nums, int k) {
int count = 0;
for (int start = 0; start < nums.length; ++start) {
int sum = 0;
for (int end = start; end >= 0; --end) {
sum += nums[end];
if (sum == k) {//找到目标值了. 为什么还不跳出内循环, 因为数组还有负数呢!sum也有可能继续==k
count++;
}
}
}
return count;
}
}
[思路分析二, 前缀和 + 双重for循环]
在求区间和时, 我们可以用前缀和来降低反复的调用或者使用前缀和带来的复杂度.
本思路先把数组的前缀和求出来并保存在preSum数组中, 再使用双重for循环遍历前缀和数组, 找到preSum[j] - preSum[i] == k的所有可能的组合.
[代码实现]
//1. 前缀和 + 双重for循环
class Solution {
public int subarraySum(int[] nums, int k) {
//前缀和求法
//首先求出前缀和数组
int len = nums.length;
int[] preSum = new int[len + 1]; //前缀和数组从0开始, 所以比原始数组大1
for(int i = 1; i <= len; i++){
preSum[i] = preSum[i - 1] + nums[i - 1]; // 从preSum[0], nums[0] 到 preSum[len]
}
//因为和为k的几个数, 数量不确定, 而且这几个数更不一定就是顺序的, 所以 preSum[i] - pre[j] = k, i和j都不是固定的
// 就需要一个二维的遍历循环, 外层循环遍历i个数的presum, 内层循环遍历 每次preSum[i]时的合适的preSum[j]
int count = 0;
for(int i = 0; i < len; i++){
for(int j = i; j < len; j++){
//
//注意偏移,因为我们的nums[2]到nums[4]等于presum[5]-presum[2]
//所以这样就可以得到nums[i,j]区间内的和
if(preSum[j + 1] - preSum[i] == k){
count++;
}
}
}
return count;
}
}
[思路分析三, 使用哈希表优化前缀和]

[代码实现]
class Solution {
public int subarraySum(int[] nums, int k) {
int[] prefixSum = new int[nums.length + 1];
prefixSum[0] = 0;
for (int i = 0; i < nums.length; i++) {
prefixSum[i + 1] = prefixSum[i] + nums[i];
}
Map<Integer, Integer> map = new HashMap<>(); // key 为前缀和,value为前缀和为key的个数,问题转化为和为k的问题
int ans = 0;
for (int i = 0; i < prefixSum.length; i++) {
// 如果有与当前prefixSum[i]的差为k的,则加上它的个数
ans = ans + map.getOrDefault(prefixSum[i] - k, 0);
// 统计前缀和的个数
map.put(prefixSum[i], map.getOrDefault(prefixSum[i], 0) + 1);
}
return ans;
}
}



浙公网安备 33010602011771号