力扣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+1 到 i 的子数组和就是 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) 简洁遍历数组
自动装箱/拆箱 int 和 Integer 自动转换

7、算法思想总结

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

8、复杂度分析

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

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

9、总结

方法 时间 空间 是否推荐
暴力双循环 O(n²) O(1) 小数据可用
前缀和 + 哈希表 O(n) O(n) 强烈推荐!面试常考
相关推荐
QianhangQianping10 分钟前
撤销重做不是「存快照」:一个可视化编辑器的Operation 设计
java·前端·数据库·typescript·编辑器·前端架构·撤销重做
海绵宝宝转agent16 分钟前
LeetCodehot100题解
java·笔记·学习·算法·leetcode·深度优先
SunnyDays101117 分钟前
Java 操作 Word 脚注和尾注:添加、读取与删除
java·word
SunnyDays101118 分钟前
Java 为 Word 添加页码:隐藏封面页码、从指定页开始编号、奇偶页页码设置等
java·word·页码
开源推荐官22 分钟前
多门店 O2O 商城选型复盘:从开发视角对比主流开源系统
java·架构·开源·php
吴名氏.23 分钟前
Java转AI Agent教程(第二周)超时重试与错误分类
java·人工智能·python·分类·ai agent
青少儿编程课堂25 分钟前
2026蓝桥杯·全国青少年信息素养大赛·电子学会Python/C++考级:堆排序与优先队列解析
c++·python·算法·bfs·信息学竞赛
从琳开始9891 小时前
从Socket到UDP回显服务器:Java网络编程完整指南
java·网络·网络协议·udp·java-ee
SZYSK1 小时前
OpenHarmony 上读卡、推理和双屏刷新抢 CPU:核验机与消费机的调度差
物联网·算法·harmonyos·1024程序员节
Ivanqhz1 小时前
MoE + LLM 应用
数据库·人工智能·pytorch·python·深度学习·算法·1024程序员节