给定一个二叉树的根节点
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)