leetcode 力扣 437 路径总和3

121 阅读1分钟

找前缀和,很抽象的一种解法。

如何在面试时快速想出来?

  1. 看看有没有前缀currSum - targetSum
  2. 保存当前currSum为前缀。
  3. 前缀数量减一,防止影响右子树。
  • currSum表示(图中简写curr),当前结点到根节点,从下往上这条路径上的值的和。
  • 看图一中左边的黄圈,绿色结点3curr值是18,表示从当前结点到根结点累积的和是18,如果curr - target = 18 - 8 = 10存在(10存在),那么和为8curr肯定存在。因为18存在,10存在,那么8肯定存在。
  • 对于结点node,哈希表中的keynodecurrSumvaluecurrSum - target的数量。dfsvalue + 1node递归完成后value - 1,防止node的左子树影响右子树。
  • ret保存每次递归返回的值。表示在当前结点node的子树中和为target的路径数量,哈希表中存在多少个curr - target,当前子树就存在多少个target。👇
ret = preSum.getOrDefault(currSum - targetSum, 0);
preSum.put(currSum, preSum.getOrDefault(currSum, 0) + 1);
  • 为什么要put <0, 1>?因为currSum - target == 0的时候,也就是key == 0时,也是一个答案。
  • 为什么上面两行代码不能交换?是为了防止target0的情况。如果交换代码,先更新哈希表,表示找到了一条curr路径,但是target0,再更新retcurr - target就不为0了。

1.jpeg

2.jpeg

class Solution {
    HashMap<Long, Integer> preSum = new HashMap<>();

    public int pathSum(TreeNode root, int targetSum) {
        preSum.put(0L, 1);

        return dfs(root, 0, targetSum);
    }

    public int dfs(TreeNode node, long currSum, int targetSum) {
        if (node == null) {
            return 0;
        }

        int ret = 0;
        currSum += node.val;

        ret = preSum.getOrDefault(currSum - targetSum, 0);
        preSum.put(currSum, preSum.getOrDefault(currSum, 0) + 1);

        ret += dfs(node.left, currSum, targetSum);
        ret += dfs(node.right, currSum, targetSum);

        preSum.put(currSum, preSum.getOrDefault(currSum, 0) - 1);

        return ret;
    }
}