一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第3天,点击查看活动详情。
一、和为K的子数组
给你一个整数数组
nums和一个整数k,请你统计并返回 该数组中和为k的子数组的个数 。
示例 1:
输入: nums = [1,1,1], k = 2
输出: 2
示例 2:
输入: nums = [1,2,3], k = 3
输出: 2
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 的 路径 的数目。
路径 不需要从根节点开始,也不需要在叶子节点结束,但是路径方向必须是向下的(只能从父节点到子节点)。
输入:root = [10,5,-3,3,2,null,11,3,-2,null,1], targetSum = 8
输出:3
解释:和等于 8 的路径有 3 条,如图所示。
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;
}