48.路径总和 III

6 阅读2分钟

题目链接

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

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

解法1 暴力遍历所有路径

思路

不需要从跟节点开始说明了可以从任意节点开始累加到 targetSum。但是我们先处理从根节点开始如何去递归。

如果当前节点为空肯定返回 0 。然后加上当前节点的值,如果为等于 targetSum 。则当前路径是可以的,然后再递归去处理左右子树。

这是 dfs 的处理路径,处理根节点开始累加到 targetSum 。而也可以从子树的节点开始累加到 targetSum。所以要递归调用原函数来处理左右子树,而且入参需要是 targetSum

代码

function pathSum(root: TreeNode | null, targetSum: number): number {
    if (!root) return 0;

    const dfs = (node, currSum) => {
        if (!node) return 0;
        let count = 0;
        currSum += node.val;
        if (currSum === targetSum) count++;

        count += dfs(node.left, currSum);
        count += dfs(node.right, currSum);
        return count;
    };

    return dfs(root, 0) + pathSum(root.left, targetSum) + pathSum(root.right, targetSum);
};

时空复杂度

时间复杂度:每个节点都要从开始计算到结尾,所以是 O(n^2)

空间复杂度:O(h)

解法2 前缀和

思路

暴力解法当中存在一些重复计算,而在当前节点去查看是否有路径之前存在过,我们可以用一个 map 来缓存,同时利用前缀和的思想。

如果当前节点路径和是 currSum,我们希望找到某个之前的 prefixSum,使得:

currSum - prefixSum = targetSum 。可以推出 prefixSum = currSum - targetSum

我们在走到当前节点时,只需要查看有没有某个祖先节点,满足它的前缀和等于 currSum - targetSum ,这条路径就符合要求。

代码

function pathSum(root: TreeNode | null, targetSum: number): number {
    if (!root) return 0;

    const prefixMap = new Map();
    prefixMap.set(0, 1);

    const dfs = (node, currSum) => {
        if (!node) return 0;

        currSum += node.val;
        const needSum = currSum - targetSum;

        let count = prefixMap.get(needSum) ?? 0;

        prefixMap.set(currSum, (prefixMap.get(currSum) ?? 0) + 1);

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

        prefixMap.set(currSum, prefixMap.get(currSum) - 1);

        return count;
    }
    return dfs(root, 0);
};

时空复杂度

时间复杂度:O(n)

空间复杂度:O(n)