累加和sum问题

750 阅读3分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第3天,点击查看活动详情

一、和为K的子数组

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

示例 1:

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

示例 2:

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

leetcode

1、分析

求数组中最长累加和为aim的子数组是多长?

子数组一定是连续的,以i结尾的子数组往左推,能推多长问题

比如目标aim = 1000,0~17位置的累加和为2500,如果0~x位置的累加和为1500(2500-1000),那么x+1 ~ 17位置就是最长子数组累加和为1000的子数组

实质是 arr[x...y]之间的累加和等于 arr[0...y](all的累加和) 之间的累加和 减去 arr[0...x-1] (Map的key)之间的累加和

arr[x...y] = arr[0...y] - arr[0...x-1]

例如:aim = 6,分析如下:

利用Map,key:数组累加和(0...x),value:数组的下标(第一次累加和为x时,此时的数组下标位置)

Map需要特殊存(0,-1),key = 0,value = -1

all为来到i位置时的累加和

0:all = -100(0~0的累加和),那么 -100-6 = -106 在不在map里,不存在,-100存进去

1:all = -98(0~1的累加和),那么 -98-6 = -104 在不在map里,不存在,-98存进去

2:all = -97(0~2的累加和),那么 -97-6 = -103 在不在map里,不存在,-97存进去

3:all = -94(0~3的累加和),那么 -94-6 = -100 在不在map里,存在,-100的小标在0位置,则数组下标1~3的子数组累加和为6

4:all = 6(0~4的累加和),那么 6-6 = 0,在不在map里,存在,0的下标在-1位置,则数组下标0~4的子数组累加和为6

所以最终0~4范围的子数组是累加和为6的最长长度

上述问题是经典模型,可以利用这个模型做改变

假如0~i位置的累加和为1500,k=1000,不就是再求有多少个累加和为500(1500-1000)的问题么

2、实现

public static int subarraySum(int[] nums, int k) {
    if (nums == null || nums.length == 0) {
        return 0;
    }
    HashMap<Integer, Integer> preSumTimesMap = new HashMap<>();
    preSumTimesMap.put(0, 1);
    int all = 0; // 0..i
    int ans = 0;
    for (int i = 0; i < nums.length; i++) {
        all += nums[i]; // 0....i 整体的前缀和
        if (preSumTimesMap.containsKey(all - k)) {
            ans += preSumTimesMap.get(all - k);
        }
        if (!preSumTimesMap.containsKey(all)) {
            preSumTimesMap.put(all, 1);
        } else {
            preSumTimesMap.put(all, preSumTimesMap.get(all) + 1);
        }
    }
    return ans;
}

二、路径总和问题

给定一个二叉树的根节点 root ,和一个整数 targetSum ,求该二叉树里节点值之和等于 targetSum 的 路径 的数目。

路径 不需要从根节点开始,也不需要在叶子节点结束,但是路径方向必须是向下的(只能从父节点到子节点)。

image.png

输入:root = [10,5,-3,3,2,null,11,3,-2,null,1], targetSum = 8
输出:3
解释:和等于 8 的路径有 3 条,如图所示。

leetcode

1、分析

此题是在二叉树上求累加和问题,二叉树的先序遍历过程中,如果累加和减去sum在Map中存在,则累加当前值,然后累加左树值,累加右树值。当返回头节点的时候,需要从map中移除all

2、实现

public class TreeNode {
    public int val;
    public TreeNode left;
    public TreeNode right;
}

public static int pathSum(TreeNode root, int sum) {
    HashMap<Integer, Integer> preSumMap = new HashMap<>();
    preSumMap.put(0, 1);
    // preAll:之前的累加和
    return process(root, sum, 0, preSumMap);
}

// 返回方法数,先序遍历
public static int process(TreeNode x, int sum, int preAll, HashMap<Integer, Integer> preSumMap) {
    if (x == null) {
        return 0;
    }
    int all = preAll + x.val;
    int ans = 0;
    // 以x.val结尾的路径有没有
    if (preSumMap.containsKey(all - sum)) {
        ans = preSumMap.get(all - sum); // 当前值
    }
    if (!preSumMap.containsKey(all)) {
        preSumMap.put(all, 1);
    } else {
        preSumMap.put(all, preSumMap.get(all) + 1);
    }
    // 左树值
    ans += process(x.left, sum, all, preSumMap);
    // 右树值
    ans += process(x.right, sum, all, preSumMap);
    if (preSumMap.get(all) == 1) { // 清理map
        preSumMap.remove(all);
    } else {
        preSumMap.put(all, preSumMap.get(all) - 1);
    }
    return ans;
}