力扣hot 100之和为 K 的子数组(Java版)

题目描述

给你一个整数数组 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

方法一:暴力

暴力枚举

先使用两层for循环遍历,然后根据变量sum和count分别累积和以及计算符合要求的子串的个数,外层循环每遍历一次都将sum置零,然后内层循环挨个遍历,计算子串个数,当sum和k相等的时候,count就+1,说明当前子串的和为k,具体代码如下:

java 复制代码
class Solution {
    public int subarraySum(int[] nums, int k) {
        int count = 0;
		for(int i=0;i<nums.length;i++){
			int sum = 0;
			for(int j = i;j<nums.length;j++){
				sum+=nums[j];
				if(sum == k ){
					count++;
				}
			}
		}
		return count;
    }
}

方法二:前缀和

1、暴力法的问题在哪?

暴力法枚举所有起点 i 和终点 j,计算子数组和,时间复杂度 O(n²)。

n = 10⁵ 时,O(n²) 就是 100 亿次操作,超时!

所以我们需要一个 线性时间 的方法。

2、引入"前缀和"思想

什么是前缀和?

前缀和 prefixSum[i] 表示从 nums[0]nums[i-1] 的和(也可以定义为到 i,看习惯)。

我们用一个变量 sum 动态记录从开头到当前位置的累计和。

比如:

java 复制代码
nums = [1, 2, 3, -2, 5]
前缀和依次为:
i=0: sum = 1
i=1: sum = 3
i=2: sum = 6
i=3: sum = 4
i=4: sum = 9

关键观察:

如果存在两个位置 j < i,使得:

复制代码
 prefixSum[i] - prefixSum[j] == k

那么从 j+1i 的子数组和就是 k

换句话说:

我们要找有多少个 j < i,使得 prefixSum[j] == prefixSum[i] - k

这就把问题转化成了:在遍历过程中,快速查找"之前出现过多少次某个前缀和值"

3、用哈希表加速查找

我们可以用一个 HashMap<Integer, Integer> 来记录:

  • key:某个前缀和的值

  • value:这个前缀和出现的次数

这样,每到一个新位置 i,我们:

  1. 计算当前前缀和 sum

  2. 查看 sum - k 在哈希表中出现了多少次 → 这就是以 i 结尾、和为 k 的子数组个数

  3. 把当前 sum 加入哈希表(计数 +1)

4、举个例子理解

复制代码
 nums = [1, 2, 3], k = 3

我们希望找到和为 3 的子数组:

  • 1,2

  • 3\] → 共 2 个

初始化:

复制代码
 map = {0: 1}   // 重要!表示前缀和为 0 出现了 1 次(空数组)
 sum = 0
 count = 0

为什么要有 map.put(0, 1)? 因为如果某个前缀和正好等于 k,比如 sum = 3, k = 3,那么 sum - k = 0,我们需要知道"前缀和 0 出现过",才能算这个子数组。所以初始放一个 0。

i = 0, num = 1
  • sum = 0 + 1 = 1

  • sum - k = 1 - 3 = -2

  • map 中没有 -2 → count += 0

  • sum=1 加入 map → map = {0:1, 1:1}


i = 1, num = 2
  • sum = 1 + 2 = 3

  • sum - k = 3 - 3 = 0

  • map.get(0) = 1 → count += 1 ✅(对应子数组 [1,2])

  • sum=3 加入 map → map = {0:1, 1:1, 3:1}


i = 2, num = 3
  • sum = 3 + 3 = 6

  • sum - k = 6 - 3 = 3

  • map.get(3) = 1 → count += 1 ✅(对应子数组 [3])

  • sum=6 加入 map → map = {0:1, 1:1, 3:1, 6:1}

最终 count = 2

5、写出代码(Java)

java 复制代码
import java.util.HashMap;
 import java.util.Map;
 ​
 class Solution {
     public int subarraySum(int[] nums, int k) {
         Map<Integer, Integer> prefixSumCount = new HashMap<>();
         prefixSumCount.put(0, 1); // 关键!处理从开头开始的子数组
         int sum = 0;
         int count = 0;
         for (int num : nums) {
             sum += num; // 当前前缀和
             // 查找是否存在前缀和 = sum - k
             if (prefixSumCount.containsKey(sum - k)) {
                 count += prefixSumCount.get(sum - k);
             }
             // 更新当前前缀和的出现次数
             prefixSumCount.put(sum, prefixSumCount.getOrDefault(sum, 0) + 1);
         }
         return count;
     }
 }

6、涉及的 Java 知识点

知识点 说明
HashMap<K, V> 用于 O(1) 时间查找和更新前缀和出现次数
getOrDefault(key, default) 如果 key 不存在,返回默认值,避免 null
增强 for 循环 for (int num : nums) 简洁遍历数组
自动装箱/拆箱 intInteger 自动转换

7、算法思想总结

思想 说明
前缀和 将子数组和问题转化为两个前缀和的差
哈希表优化 用空间换时间,避免重复计算
数学转化 sum[i] - sum[j] = ksum[j] = sum[i] - k
边界处理 初始放入 0:1 是关键技巧

8、复杂度分析

  • 时间复杂度:O(n) ------ 只遍历一次数组,每次哈希操作 O(1)

  • 空间复杂度:O(n) ------ 哈希表最多存 n 个不同前缀和

9、总结

方法 时间 空间 是否推荐
暴力双循环 O(n²) O(1) 小数据可用
前缀和 + 哈希表 O(n) O(n) 强烈推荐!面试常考
相关推荐
%Leo2 小时前
macos idea 插件搜索不到
java·intellij-idea
苏渡苇2 小时前
枚举的高级用法——用枚举实现策略模式和状态机
java·单例模式·策略模式·枚举·状态机·enum
鱼鳞_2 小时前
Java学习笔记_Day19
java·笔记·学习
Byte不洛2 小时前
LeetCode中经典双指针题(环形链表 + 快乐数 + 移动零)
算法·leetcode·链表·数组·双指针
Boop_wu2 小时前
[Java 算法] 快速排序和快速选择排序(※)
数据结构·算法·排序算法
人间打气筒(Ada)2 小时前
「码动四季·开源同行」golang:负载均衡如何提高系统可用性?
算法·golang·开源·go·负载均衡·负载均衡算法
曹牧2 小时前
Java:驱动程序无法通过使用安全套接字层(SSL)加密与 SQL Server 建立连接
java·开发语言·ssl
cch89182 小时前
PHP vs C++:10倍性能差距的编程语言对决
android·java·开发语言
司马万2 小时前
RUST基础1----数据类型
开发语言·算法·rust