力扣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,使得 prefixSumj == prefixSumi - 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) 强烈推荐!面试常考
相关推荐
用户298698530143 小时前
Word 文档字符级格式化:Java 实现方案详解
java·后端
复杂网络3 小时前
论最小 Agent 计算机的形态
算法
笨鸟飞不快3 小时前
从单个服务到集群:一次完整的性能排查复盘
java·前端
荣码4 小时前
用Streamlit给AI应用套个界面,10行代码出Web页面
java·python
SamDeepThinking4 小时前
Java微服务练习方式
java·后端·微服务
朦胧之14 小时前
AI 编程-老项目改造篇
java·前端·后端
kisshyshy19 小时前
🍦 雪糕、食堂、火车厢:三幅漫画吃透栈、队列与链表
javascript·算法
程序猿大帅19 小时前
别再只当调包侠了:用 Spring AI 落地 Function Calling,我被大模型硬生生砸出了三个大坑
java
程序员晓琪20 小时前
约定大于配置:基于 Java 包名自动生成 API 版本路由的最佳实践
java·spring boot·后端
Flittly20 小时前
【AgentScope Java新手村系列】(11)中断与恢复
java·spring boot·spring