这一题比较明显的需要使用dfs来解决
错误代码
这里的代码没有考虑到每个节点开始遍历的情况
var pathSum = function (root, targetSum) {
let cnt = 0
var dfs = function (node, sum) {
if (!node) return
if (sum === targetSum) {
cnt++
sum = 0
}
dfs(node?.left, node?.val + sum)
dfs(node?.right, node?.val + sum)
}
dfs(root, 0)
return cnt
};
正确代码
- DFS (深度优先搜索) :
dfs函数从当前节点开始,计算从该节点出发的路径和是否等于targetSum。- 如果路径和等于
targetSum,则计数器count增加。 - 然后递归检查左子树和右子树。
- 遍历整个树:
traverse函数从树的每个节点开始调用dfs,确保我们从每个节点出发都进行了路径和的检查。- 这样可以确保所有路径都被遍历到,不遗漏任何可能的路径。
- 计数器:
- 最后返回计数器
count,它包含所有路径和等于targetSum的路径数量。 这种方法的时间复杂度是O(n^2),因为每个节点都要作为起点进行一次 DFS 搜索,但对于一般的树结构来说,这种方法是可行的。
- 最后返回计数器
var pathSum = function (root, targetSum) {
let cnt = 0
var dfs = function (node, sum) {
if (!node) return
sum += node.val
if (sum === targetSum) {
cnt++
}
dfs(node.left, sum)
dfs(node.right, sum)
}
// 测试每一个节点
var traverse = function(node){
if(!node) return
dfs(node, 0)
traverse(node.left)
traverse(node.right)
}
traverse(root)
return cnt
};
前缀和解法
-
前缀和:
currentSum记录当前路径的前缀和。- 如果在当前路径上存在一个前缀和等于
currentSum - targetSum,说明从该前缀和到当前节点的路径和为targetSum。
-
哈希表
map:- 这个哈希表用来存储每个前缀和出现的次数。
- 通过查找
currentSum - targetSum是否在哈希表中存在,可以快速判断是否有符合条件的路径。 - 每次递归调用后,当前节点的前缀和计数会增加或减少,以保证每条路径计算的独立性。
-
回溯:
- 当从子节点返回时,需要恢复当前节点的前缀和计数,即从哈希表中减去当前节点的贡献,以便不影响其他路径的计算。
-
时间复杂度:
- 该方法的时间复杂度是
O(n),因为每个节点只被访问一次,且哈希表的操作都是常数时间。
- 该方法的时间复杂度是
var pathSum = function (root, targetSum) {
let cnt = 0
let map = new Map()
map.set(0, 1)
var dfs = function (node, currentSum) {
if (!node) return
currentSum += node.val
if (map.has(currentSum - targetSum)) {
cnt += map.get(currentSum - targetSum)
}
map.set(currentSum, (map.get(currentSum) || 0) + 1)
dfs(node.left, currentSum)
dfs(node.right, currentSum)
map.set(currentSum, map.get(currentSum) - 1)
}
dfs(root, 0)
return cnt
};