【LeetCode】560. 和为K的子数组(前缀和+哈希表)

题目链接

https://leetcode-cn.com/problems/subarray-sum-equals-k/

题目描述

给定一个整数数组和一个整数 k,你需要找到该数组中和为 k 的连续的子数组的个数。

示例 1 :
输入:nums = [1,1,1], k = 2
输出: 2 , [1,1] 与 [1,1] 为两种不同的情况。

说明 : 数组的长度为 [
1, 20,000]。 数组中元素的范围是 [-1000, 1000] ,且整数 k 的范围是 [-1e7, 1e7]。

解题思路

1.暴力枚举

数组、字符串的题目,通常可以尝试使用暴力枚举的方式。从数组中每个下标开始,不断向后累加数组元素,当累加数组元素和等于K,则将ans++。

该方法的时间复杂度为O(n2)。

2.前缀和

这次最初的想法是想使用滑动窗口,当滑动窗口的值 < K,则将滑动窗口扩张,即将滑动窗口右侧边界右移;当滑动窗口的值 > K;则收缩滑动窗口,即将滑动窗口的左边界右移动。当时由于本题中数组元素的值可以出现负数,而我想到的滑动窗口法适用于数组中的元素要么全部大于0,要么全部小于0。所以此法行不通。

前缀和思路:

通过前缀和的想法,把问题转变为

有几种i、j的取值,可以使得使得从第 i 到 j 项的子数组的求和 == k。

有几种i、j的取值,可以使得前缀和数组之差 == K。

所以本体的解法在于,先求出所有前缀和数组中的值,然后在前缀和数组中寻找i、j两个下标对应的值满足i < j && == K。

时间复杂度还是O(n2)。可以参考LC 两数之和 利用哈希表进行优化。

以下这张图是对前缀和的理解:

3.前缀和优化

我们的目标是要求出前缀和数组中某两个元素之差为K,这个目标与LC中两数之和类似,在LC两数之和中,要求我们在数组中找出某两个元素和为P;等价于当其中一个元素为x,数组中是否存在p-x这个元素,这显然可以采用map进行处理,本题同理。

AC代码

1.暴力枚举法(超时)

 1 class Solution {
 2 public:
 3     int subarraySum(vector<int>& nums, int k) {
 4         int cnt = 0;
 5         for(int i = 0; i < nums.size(); i ++){
 6             int sum = 0;
 7             for(int j = i; j < nums.size(); j ++){
 8                 sum += nums[j];
 9                 if(sum == k){
10                     cnt ++;
11                 }
12             }
13         }
14         return cnt;
15     }
16 };

2.前缀和(超时)

 1 class Solution {
 2 public:
 3     int subarraySum(vector<int>& nums, int k) {
 4         vector<int> presum(nums.size()+1);
 5         for(int i = 1; i < presum.size(); i++) presum[i] = presum[i-1]+nums[i-1]; //计算前缀和数组的值
 6         int ans = 0;
 7         for(int i = 1; i < presum.size(); i++) //双重for循环计算前缀和数组中某两个元素差值为K
 8         {
 9             for(int j = i; j < presum.size(); j++)
10             {
11                 if(presum[j] - presum[i-1] == k) ans++;
12             }
13         }
14         return ans;
15     }
16 };

3.前缀和优化

 1 class Solution {
 2 public:
 3     int subarraySum(vector<int>& nums, int k) {
 4         if(nums.size()==1 && nums[0] == k) return 1;
 5         if(nums.size() == 1 && nums[0] != k) return 0;
 6         vector<int> presum(nums.size()+1);
 7         presum[0]= 0;
 8         for(int i = 1; i < presum.size(); i++)
 9         {
10             presum[i] = presum[i-1] + nums[i-1];
11         }
12         map<int,int> mp;
13         int ans = 0;
14         for(int i = 0; i < presum.size(); i++)
15         {
16             if(mp[presum[i] - k])
17             {
18                 ans += mp[presum[i] - k];
19             }
20             mp[presum[i]]++; //该行代码必须在if判断语句之后,否则当出现K=0时,不能通过测试用例,例如[-1,-1,1],0 在循环内部,每条语句的顺序都至关重要
21         }
22         return ans;
23     }
24 };

 

posted @ 2020-05-16 12:09  控球强迫症  阅读(296)  评论(0编辑  收藏  举报