题目链接
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 };