560. 和为 K 的子数组 - 力扣(LeetCode)https://leetcode.cn/problems/subarray-sum-equals-k/description/
题目描述:
给你一个整数数组 nums
和一个整数 k
,请你统计并返回该数组中和为 k 的子数组的个数 。子数组是数组中元素的连续非空序列。
题目解析:
根据题目我们可以得知本题要求我们找到和为 k 的非空连续子序列的个数并将个数返回。
例如:假设nums:{1,2,2,3} k : 3
我们可以从中找出三组和为 k 的数组,但是因为题目要求的是连续且非空所以只有1,3两组符合题意,结果返回 2。
暴力求解:
直接根据题目的要求挨个求和判断结果是否为 k ;
class Solution {
public int subarraySum(int[] nums, int k) {
//用来记录符合题意的数组个数
int count = 0;
int len = nums.length;
//直接暴力求和
for (int i = 0; i < len; i++) {
int tmp = 0;
for (int j = i; j < len; j++) {
tmp += nums[j];
if (tmp == k) {
count++;
}
}
}
return count;
}
}
它好歹是个中等难度的题这道题用暴力解法可以通过是我没有想到的。
前缀和优化:
虽然暴力解法可以通过但是用时是非常长的,因为题目并只要求我们返回符合题意的数组数目,所以我们可以利用前缀和来对暴力解法进行优化:
- 第一步:先求出每个位置的前缀和;
- 第二步:找出前缀和数组中值为 k 的个数;
- 第三步:前缀和数组中的每位两两相减找出差为 k 的个数;
- 第四步:返回第二步和第三步找出的数目之和;
class Solution {
public int subarraySum(int[] nums, int k) {
int l = 0;
int r = 0;
int len = nums.length;
//用来统计符合题意的数组个数
int count = 0;
//利用循环来求出前缀和数组
for (int i = 0; i < len; i++) {
if (i != 0) {
nums[i] = nums[i] + nums[i-1];
}
//判断改位是否等于k
if (nums[i] == k) {
count++;
}
}
//使前缀和数组中的每位两两相减找出差为 k 的个数
for (int i = 0; i < len; i++) {
for (int j = i+1; j < len; j++) {
if (nums[j] - nums[i] == k) {
count++;
}
}
}
return count;
}
}
前缀和+哈希表优化:
上面的优化感觉是负优化,但其实是为了这一步做的铺垫我们可以利用公式: Xi - k = Y 来进行优化,Xi 表示前缀和数组的第 i 位;只要我们知道第 i 位之前有多少个值为 Y 的数就行了。所以我们可以引入哈希表来存储第 i 位之前的数;
class Solution {
public int subarraySum(int[] nums, int k) {
int count = 0;
//创建一个哈希表 来存储第 i 位之前的数
Map<Integer, Integer> hash = new HashMap<>();
//此处存储一个 0 是必须的,
//因为有可能第 i 位刚好等于 k;
hash.put(0, 1);
//此处利用sum来优化了前缀和数组。
int sum = 0;
int len = nums.length;
for (int i = 0; i < len; i++) {
//求第i位的前缀和
sum += nums[i];
//判断哈希表中是否有符合题意的 Y ;
//如果没有就将第i位的前缀和放入哈希表
if (hash.containsKey(sum-k)) {
int tmp = hash.get(sum-k);
count += tmp;
}
if (hash.containsKey(sum)) {
int tmp = hash.get(sum);
hash.put(sum,tmp+1);
}
else {
hash.put(sum,1);
}
}
return count;
}
}
感谢浏览