• 2.25
    • 用 map 记录前缀和和其次数,不需要手动添加,在 for 中循环边判断边加入
class Solution {
    public int subarraySum(int[] nums, int k) {
        int res = 0;
        int preSum=0;
        Map<Integer,Integer> map = new HashMap<>();
        map.put(0,1);
        for(int i=0;i<nums.length;i++){
            int num = nums[i];
            preSum+=num;
            if(map.containsKey(preSum-k)){
                res+=map.get(preSum-k);
            }
            map.put(preSum,map.getOrDefault(preSum,0)+1);
        }
        return res;
    }
}
  • 2.6

    • 找 preSum-k,返回 count
  • 1.23

    • 前缀和,数学问题,找 preSum-k(key),次数(value)
  • 11.18

    • 中上难度,像 map 里记录的是 presum 当前的前缀和,我们想得到的哪个前缀和只是作为判断条件,来记录 count 值
    • 不管怎样,都需记录当前 presum 的前缀和,需要用到 put(x,getorDefualt())
    • 不管怎样,需要初始化第一个情况,前缀和为 0 的出现了一次。

1. 直观比喻:剪绳子

假设你手头有一根很长的绳子(数组),你一直往右走,每走一步就记录一下**“从头开始到现在”的总长度**(这就是 preSum)。

  • 现在的任务:你想从这根长绳子里,剪出一段长度正好为 k 的绳子。

  • 你当前的位置:假设你现在走到了 10 米处(当前 preSum = 10)。

  • 目标:你要剪一段 k = 3 米的绳子,并且这段绳子的终点就是你脚下

问题来了:这一剪刀应该剪在哪里?

答案:显然是 10 - 3 = 7 米处。

只要之前的记录里,你曾经在 7 米处做过记号(即 Map 里存过 preSum 为 7 的记录),那就说明从那个记号点到你脚下,正好有一段长度为 3 的绳子。


2. 数学推导(公式化理解)

preSum[i] 为数组 nums[0] 加到 nums[i] 的和。

如果你想要一段子数组 nums[j...i] 的和等于 k

$$nums[j] + \dots + nums[i] = k$$

这段子数组的和,其实可以用两个前缀和相减得到:

$$preSum[i] - preSum[j-1] = k$$

  • preSum[i]:从头加到现在的和(当前的长度)。

  • preSum[j-1]:想要去掉的前面的那一截和(需要剪掉的历史长度)。

我们把公式移项一下,就变成了代码里的逻辑:

$$preSum[j-1] = preSum[i] - k$$

翻译成人话

“我现在手里的总和是 preSum,我想知道有没有一段子数组和是 k。我就回头看一看,历史上(Map里)有没有出现过一个前缀和等于 preSum - k。如果有,把他减掉,剩下的就是 k 了。”

class Solution {
    public int subarraySum(int[] nums, int k) {
        Map<Integer,Integer> map = new HashMap<>();
        map.put(0,1);
        int count = 0;
        int preSum = 0;
        for(int num:nums){
            preSum+=num;
            //如果有前缀和为preSum-k,说明存在一段子数组和为k
            if(map.containsKey(preSum-k)){
                count+=map.get(preSum-k);//这里需要找到所有map中v值=preSum-k的数量
            }
            //记录当前前缀和出现次数
            //如果map中原来有1=2,经过map.put(1, 3)后会覆盖为1=3
            map.put(preSum,map.getOrDefault(preSum,0)+1);
        }
        return count;
    }
}