It hurts to remember,but it would be worse to forget.
铭记虽痛苦,但遗忘更糟糕。
问题描述
来源:LeetCode第560题
难度:中等
给定一个整数数组和一个整数k,你需要找到该数组中和为k的连续的子数组的个数。
示例 1 :
输入:nums = [1,1,1], k = 2
输出: 2 , [1,1] 与 [1,1] 为两种不同的情况。
说明 :
- 数组的长度为[1,20,000]。
- 数组中元素的范围是[-1000,1000],且整数k的范围是[-1e7, 1e7]。
暴力求解
暴力求解是最容易想到的,枚举数组nuns的所有子数组,然后统计所有子数组和等于k的个数
来看下代码
public int subarraySum(int[] nums, int k) {
2 int count = 0;
3 for (int j = 0; j < nums.length; j++) {
4 for (int i = 0; i <= j; i++) {
5 int sum = 0;
6 //计算子数组[i……j]中所有数字的和
7 for (int m = i; m <= j; m++) {
8 sum += nums[m];
9 }
10 //如果子数组[i……j]中所有数字
11 //的和等于k,count加1
12 if (sum == k)
13 count++;
14 }
15 }
16 return count;
17}
时间复杂度:O(n^3)。
空间复杂度:O(1)。
这种时间复杂度太高,当数据量比较大的时候,很容易超时,我们再来优化一下。当我们以nums[j]为子数组最后一个元素的时候,不用每次都枚举子数组[i……j]之间所有元素的和,只需要以nums[j]为最后一个元素,从后往前累加,即可计算以nums[j]为最后一个元素的连续子数组。比较绕,来看个图
来看下代码
public int subarraySum(int[] nums, int k) {
2 int count = 0;
3 for (int j = 0; j < nums.length; j++) {
4 //sum是以nums[j]为最后一个元素,
5 //从后往前累加的值
6 int sum = 0;
7 for (int i = j; i >= 0; i--) {
8 sum += nums[i];
9 //如果子数组的和等于k,count就加1
10 if (sum == k) {
11 count++;
12 }
13 }
14 }
15 return count;
16}
时间复杂度:O(n^2)。
空间复杂度:O(1)。
时间复杂度从n^3降到了n^2,我们再来看一个时间复杂度为n的解决方式,就是前缀和。
前缀和解决
所谓前缀和就是数组中前面n个元素的和,比如前缀和pre[i]的值是:
pre[i]=nums[0]+nums[1]+……+nums[i];
前缀和pre[j]的值是:
pre[j]=nums[0]+nums[1]+……+nums[i]+nums[i+1]+……+nums[j];
如果我们要求子数组[i……j]之间所有元素的和,也就是
nums[i]+nums[i+1]+……+nums[j]=pre[j]-pre[i-1];
也就是说如果pre[j]-pre[i-1]等于k,说明我们找到了一个和为k个连续子数组,这就变成了两数之和问题。因为k的值是固定的,如果枚举pre[j],我们只需要统计pre[i-1]的个数即可,这个统计方式可以使用map来解决,看下代码(这里为了方便计算,pre长度增加1,pre[0]=0)
public int subarraySum(int[] nums, int k) {
2 //先计算前缀和,pre[i]表示数组nums中前i个元素的和
3 int[] pre = new int[nums.length + 1];
4 for (int i = 0; i < nums.length; i++) {
5 pre[i + 1] = pre[i] + nums[i];
6 }
7
8 int count = 0;
9 Map<Integer, Integer> map = new HashMap<>();
10 for (int j = 0; j <= nums.length; j++) {
11 //计算pre[i-1]+pre[j]=k,我们只需要找出pre[i-1]
12 //的个数即可,这个可以通过map来查找
13 int other = pre[j] - k;
14 if (map.containsKey(other)) {
15 //如果map中存在pre[i-1],把他的个数进行累加
16 count += map.get(other);
17 }
18 //pre[j]的个数加1在放到map中
19 map.put(pre[j], map.getOrDefault(pre[j], 0) + 1);
20 }
21 return count;
22}
时间复杂度:O(n)。
空间复杂度:O(n)。