题目描述
给你一个整数数组 nums 和一个整数 k ,请你统计并返回 该数组中和为 k 的子数组的个数 。
子数组是数组中元素的连续非空序列。
示例 1:
输入:nums = [1,1,1], k = 2
输出:2
示例 2:
输入:nums = [1,2,3], k = 3
输出:2
提示:
1 <= nums.length <= 2 * 104-1000 <= nums[i] <= 1000-107 <= k <= 107
解法一
思路:
枚举算法,从下标i开始,统计后面所有位置的字串和,当中若出现和为k则个数加一
代码:
public class leetcode_010 {public static int subarraySum(int[] nums, int k) {int ans=0;int tempSum=0;for(int i=0;i<nums.length;i++){tempSum+=nums[i];if(tempSum==k)ans++;for (int j=i+1;j<nums.length;j++){tempSum+=nums[j];if(tempSum==k)ans++;}tempSum=0;}return ans;}public static void main(String[] args) {Scanner sc = new Scanner(System.in);String[] line = sc.nextLine().split(",");int[] nums= Arrays.stream(line).mapToInt(Integer::parseInt).toArray();int k = sc.nextInt();int res = subarraySum(nums,k);System.out.println(res);}
}
解法二
思路:
来自官方的解答,采用前缀和和哈希表进行优化,时间复杂度为O(n)。
- 前缀和(Prefix Sum):定义
pre[i]为数组从索引0到i的所有元素之和。例如,对于数组nums,pre[i] = nums[0] + nums[1] + ... + nums[i]。 - 子数组和:子数组
[j..i]的和可以表示为pre[i] - pre[j-1]。注意,当j=0时,pre[j-1]定义为0(即没有元素时的前缀和)。 - 目标条件:我们想找到所有满足
pre[i] - pre[j-1] == k的子数组。这等价于pre[j-1] == pre[i] - k。因此,对于每个i,我们需要统计有多少个j(其中j从0到i)使得pre[j-1]等于pre[i] - k。
为了高效地统计这些 j 的个数,我们使用一个哈希表(如 HashMap),键是前缀和的值,值是该前缀和出现的次数。这样,对于每个 i,我们可以直接在哈希表中查找 pre[i] - k 的出现次数,从而避免重复计算。
步骤详解
- 初始化:
- 创建一个哈希表
map,用于存储前缀和及其出现次数。 - 初始时,放入前缀和
0,出现次数为1。这是因为当没有元素时,前缀和为0,且这相当于j=0时的pre[j-1] = 0。 - 初始化当前前缀和
pre = 0和计数器count = 0。
- 创建一个哈希表
- 遍历数组:
- 对于每个元素
nums[i]:- 更新当前前缀和:
pre = pre + nums[i]。 - 计算目标值
target = pre - k。这个目标值就是我们要在哈希表中查找的pre[j-1]。 - 如果
target存在于哈希表中,则说明存在若干下标j(对应pre[j-1] = target),使得子数组[j..i]的和为k。因此,将count加上map.get(target)。 - 将当前前缀和
pre加入哈希表:如果pre已存在,则将其出现次数加1;否则,放入pre并设置次数为1。
- 更新当前前缀和:
- 对于每个元素
- 返回结果:
- 遍历结束后,
count即为所有和为k的连续子数组的个数。
- 遍历结束后,
遍历的过程中,我们先求出前缀和,当下标来到i时,我们求pre[i]-k出现的次数,即需要统计有多少个 j(其中 j 从 0 到 i)使得 pre[j-1] 等于 pre[i] - k。这些前缀和在我们求pre[i]前就已经求出。
为什么pre[i] - k出现的次数等于多少个j,因为我们从头开始求前缀和,[0i]只会统计一次,当出现相同的前缀和时,是不用的[0i]计算得来的。
代码:
public class Solution {public int subarraySum(int[] nums, int k) {int count = 0, pre = 0;HashMap < Integer, Integer > mp = new HashMap < > ();mp.put(0, 1);for (int i = 0; i < nums.length; i++) {pre += nums[i];if (mp.containsKey(pre - k)) {count += mp.get(pre - k);}mp.put(pre, mp.getOrDefault(pre, 0) + 1);//若当前前缀和不存在,则创建该前缀和,出现次数置为1,若出现过这个前缀和,则该前缀和出现的次数加1}return count;}
}
