题目描述:560和为K的子数组
给定一个整数数组和一个整数 k,你需要找到该数组中和为 k 的连续的子数组的个数。
示例 1 :
输入:nums = [1,1,1], k = 2
输出: 2
说明: [1,1] 与 [1,1] 为两种不同的情况。
示例2:
输入:nums = [1,1,1,0], k = 2
输出: 3
说明:[1,1],[1,1],[1,1,0]都是
示例3:
输入:nums = [-1,1,1,0], k = 2
输出: 2
说明:[1,1],[1,1,0]都是
题目要求很好理解,但是我们首先要考虑到所有会发生的情况,因为是int型数组,所以里面的整数类型无非就是小于零,等于0,大于零。所以我们做这个题目的时候需要重点考虑一种情况,那就是数组中包含0的情况,比如示例2,[1,1],[1,1,0]都为其子数组,不能漏掉这个情况。有些刚开始刷题的同学,可能想不到这种情况,而且原题示例里面只给了一个例子,所以某些特殊情况想不到,不过随着你刷题数目的增加,你分析题目的能力则会越来越强,拿到一个题首先就能想到其中的一些特殊例子。
首先我们先说一下暴力解法
注:暴力法我们比较容易想到的,但是暴力法的时间复杂度往往会比较高,虽然时间复杂度比较高,但是我们也一定不要忽略暴力解法(尤其是遇到没太有思路的题目)而且很多时候其他解法是对暴力法的优化,所以我们做题时候不要眼高手低,忽略暴力法。而是应该试着对暴力方法进行优化。
暴力法的思路很简单,就是双重循环,对nums[j]进行累加,当和为K值时,计数器加1。遍历结束后,返回数值。暴力方法对0存在的情况同样适用,因为sum+0,值仍为sum,如果此时sum等于k,则计数器仍会加一。
题目代码:
class Solution {
public int subarraySum(int[] nums, int k) {
if(nums.length == 0){
return 0;
}
//计数器
int count = 0;
//代表的是蓝指针的运动轨迹
for(int i =0;i<nums.length;i++){
int sum = 0;
//代表橙指针的运动轨迹
for(int j =i;j<nums.length;j++){
//将橙指针遍历的值进行相加
sum += nums[j];
//等于时计数器加1
if(sum == k){
count++;
//这里不可以break,因为可能有这种情况k=1数组为[1,0,0,1,-1]这个情况
}
}
}
return count;
}
}
Hashmap法:
这个方法比较巧妙,leetcode中有好几个题都可以使用这个方法,所以希望读者可以认真看下这个方法,可能刚开始刷题的时候看起来有些吃力,不过我们可以一起讨论。
我们来看一下sum的值的变化
sum[0]=nums[0];
sum[1]=nums[0]+nums[1];
sum[2]=nums[0]+nums[1]+nums[2];
sum[3]=nums[0]+nums[1]+nums[2]+nums[3];
因为sum[3]=sum[2]+nums[3],所以sum[3]-sum[2]=nums[3],sum[3]-sum[1]=nums[2]+nums[3];
通过上式我们知道了sum[j]-sum[i]=k,变形得sum[j]-k=sum[i];这个题目也就变成了求sum[i]的出现次数了。
class Solution {
public int subarraySum(int[] nums, int k) {
int count = 0;//计数器
if(nums.length < 1){
return 0;
}
//hashmap用来存sum[i]的和出现的次数
HashMap<Integer, Integer> map = new HashMap<>();
map.put(0,1);//防止这种情况,数组只有一个1,k=1则count=1
int sum = 0;
for(int i = 0; i<nums.length; i++){
sum += nums[i];//累加
//获取sum-k出现的个数,例当前指针在第j位。sum[j]=10,k=2,
//则需要知道sum[i]=8时,共有几种情况,sum[i]代表的是前i位的值
if(map.containsKey(sum - k)){
count += map.get(sum-k);//获取次数
}
map.put(sum,map.getOrDefault(sum,0)+1);//没出现则存入一次
}
return count;
}
}