相关题目:
题目描述:
给定一个整数数组和一个整数 k,你需要找到该数组中和为 k 的连续的子数组的个数。
示例 1 :
输入:nums = [1,1,1], k = 2
输出: 2 , [1,1] 与 [1,1] 为两种不同的情况。
说明 :
数组的长度为 [1, 20,000]。
数组中元素的范围是 [-1000, 1000] ,且整数 k 的范围是 [-1e7, 1e7]。
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/subarray-sum-equals-k
思路.1:
1.这题首先想到的是暴力,三重循环,就是时间复杂度有点高,就不写了,
看怎么来优化暴力。我们看的是某段区间里的和是否为K,假设这个区间是
[i~j],按照暴力的做法就是定一个边界然后缩小边界,看看边界内是否有
个区间的和为k。前缀和正好就符合这个条件,sum[j]-sum[i-1]=k,然后我
们就要统计这个区间有多少个,这样的时间复杂度是O(n)2;
2.O(n)2的复杂的也是有点高,如果想要O(n)的时间复杂度,怎么做呢?用
前缀和的做法是这样的
int count=0;
for(int j=1;j<=length;j++){
for(int i=1;i<=j;i++){
if(sum[j]-sum[i-1]==k){
count++;
}
}
}
我们要从以i为右边界里找,有多少个以j为左边界的区间使得sum[j]-
sum[i-1]=k,想减掉i这重循环,要从(sum[j]-sum[i-1]=k)这个
等式来看,sum[i-1]=sum[j]-k,这样改等式可以变成(sum[j]-
(sum[j]-k)=k)这样我们就可以把i“变没”,我们只需要找到以j为右
边间的区间里,sum[j]-k出现的次数然后相加,这里用Hashmap不用
数组来保存sum[j]-k出现的次数,因为有它可能是负数;
3.这里要注意的一点是hashmap要有初始值hashmap.put(0, 1),
关于这个我在网上看到了几个很好的理解方法:
3.1因为数组中有些数直接就等于k(特例帮助理解)
3.2这是因为要保持哈希表的定义, key 是当前位置之前的所有元素的和(依然是前缀和),value 是对应的个数。在遍历开始之前,当前位置之前的所有元素为空,可以认为和是 0,对应的个数就为 1
(分别来自liweiwei大佬和Sumail🍒大佬)
代码:
import java.util.HashMap;
import java.util.Map;
public class Solution {
public int subarraySum(int[] nums, int k) {
// key:前缀和,value:key 对应的前缀和的个数
Map<Integer, Integer> countsum = new HashMap<>();
// 对于下标为 0 的元素,前缀和为 0,个数为 1
countsum.put(0, 1);
int sum = 0;
int count = 0;
for (int num : nums) {
sum += num;
// 先获得前缀和为sum - k 的个数
if (countsum.containsKey(sum - k)) {
count += countsum.get(sum - k);
}
// 之前出现过值就加1,没出现个值就为1
countsum.put(sum, countsum.getOrDefault(sum, 0) + 1);
}
return count;
}
}
相比于前缀和快了不少