欢迎光临
我们一直在努力

hot 100 第十题 10.和为K的子数组

题目:

给你一个整数数组 nums 和一个整数 k ,请你统计并返回 该数组中和为 k 的子数组的个数 。

子数组是数组中元素的连续非空序列。

示例 1:

输入:nums = [1,1,1], k = 2
输出:2

示例 2:

输入:nums = [1,2,3], k = 3
输出:2

核心思路:

子数组的和 = 当前前缀和 – 之前某个前缀和。

什么是前缀和?

前缀和就是从数组开头一直累加到当前位置的和:

nums = [1, 1, 2, 3]
前缀和: [1, 2, 4, 7]

前缀和[0] = 1
前缀和[1] = 1+1 = 2
前缀和[2] = 1+1+2 = 4
前缀和[3] = 1+1+2+3 = 7

前缀和的用处

任何一个连续子数组的和,都可以用两个前缀和相减得到:

nums = [1, 1, 2, 3]
前缀和: [1, 2, 4, 7]

想求 [1, 2] 的和?
不需要重新累加,直接:前缀和[2] – 前缀和[0] = 4 – 1 = 3

想求 [2, 3] 的和?
前缀和[3] – 前缀和[1] = 7 – 2 = 5

可以画成这样:

nums = [1, 1, 2, 3]
├───┤
前缀和[0]=1
├───────┤
前缀和[2]=4

[1, 2] 的和 = 前缀和[2] – 前缀和[0] = 4 – 1 = 3

怎么找和等于 k 的子数组?

既然 子数组的和 = 当前前缀和 – 之前某个前缀和,那么:

子数组的和 = k

当前前缀和 – 之前某个前缀和 = k

之前某个前缀和 = 当前前缀和 – k

所以每走到一个位置,只需要看之前有没有出现 sum – k 这个前缀和就够了。

完整演示

nums = [1, 1, 2], k = 2
用 map 记录之前出现过的前缀和及其次数
初始: map = {0: 1} (下面解释为什么)

i=0: sum = 1
找 sum-k = 1-2 = -1, map 里没有 ✗
map = {0:1, 1:1}

i=1: sum = 2
找 sum-k = 2-2 = 0, map 里有 0 ✓ count=1
→ 对应子数组 [1,1],和 = 2-0 = 2
map = {0:1, 1:1, 2:1}

i=2: sum = 4
找 sum-k = 4-2 = 2, map 里有 2 ✓ count=2
→ 对应子数组 [2],和 = 4-2 = 2
map = {0:1, 1:1, 2:1, 4:1}

结果: 2

题解:

class Solution {
public int subarraySum(int[] nums, int k) {
int count = 0;
int sum = 0;
// key: 前缀和, value: 出现次数
HashMap<Integer, Integer> map = new HashMap<>();
map.put(0, 1); // 初始化:和为0出现一次

for (int i = 0; i < nums.length; i++) {
sum += nums[i]; // 当前前缀和
if (map.containsKey(sum – k)) { // 检查是否存在 sum-k
count += map.get(sum – k); // 累加次数
}
map.put(sum, map.getOrDefault(sum, 0) + 1); // 记录当前前缀和
}

return count;
}
}
```

## 核心思路

核心在于一个公式:**`子数组和 = 当前前缀和 – 之前某个前缀和`**

前缀和就是从头到当前位置的累加和:
```
nums = [1, 1, 2]
前缀和: [1, 2, 4]
↑ ↑ ↑
s[0] s[1] s[2]
```

如果要求 `[1, 2]` 这个子数组的和,不需要重新累加,直接用前缀和相减:
```
sum[1,2] = s[2] – s[0] = 4 – 1 = 3
```

所以当我们走到位置 `i`,当前前缀和是 `sum`,只需要看**之前有没有出现 `sum – k` 的前缀和**,有的话就说明中间那段子数组和刚好等于 `k`:
```
nums = [1, 1, 2], k = 2
前缀和: [1, 2, 4]

i=0: sum=1, sum-k=1-2=-1, map里没有 -1 ✗
i=1: sum=2, sum-k=2-2=0, map里有 0(初始化的) ✓ count=1
→ 对应子数组 [1,1],和 = 2-0 = 2
i=2: sum=4, sum-k=4-2=2, map里有 2 ✓ count=2
→ 对应子数组 [2],和 = 4-2 = 2

为什么要初始化 ?map.put(0, 1)

当前缀和本身就等于 时,如果 map 里没有 ,就会漏掉从数组开头开始的子数组:ksum – k = 00

<span style="color:#abb2bf"><code>nums = [2], k = 2

i=0: sum = 2
找 sum-k = 2-2 = 0
如果 map 里没有 0 → 找不到,漏掉了!
如果 map 里有 0 → 找到了,对应子数组 [2]</code></span>

map.put(0, 1)相当于在数组开头放一个"虚拟的前缀和 0"。

本质

比较粗暴的方法是双重循环枚举所有子数组,O(n²)。前缀和的本质就是把“求子数组的和”这件事转化为“在 map 里找一个数”,每个位置只做一次查找,所以降到了 O(n)。

赞(0)
未经允许不得转载:171主机测评 » hot 100 第十题 10.和为K的子数组
分享到: 更多 (0)

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址