路径总和Ⅰ
题目:给你二叉树的根节点 root 和一个表示目标和的整数 targetSum ,判断该树中是否存在 根节点到叶子节点 的路径,这条路径上所有节点值相加等于目标和 targetSum 。
叶子节点:是指没有子节点的节点。
解析:要找到满足要求的路径,必须要满足
- 根节点到叶子节点(叶子节点left,right都为null)
- 路径上所有节点值相加等于targetSum
从头到尾,我们就使用深度优先遍历,那就使用前序遍历。看完题目细想一下,我觉得从根节点到叶子节点这个条件很关键,这就意味着在判断途中,遇到相加等于目标的路径的时候,我们还要判断这条路是不是到底了。反过来看,我们只需要找出一路到底的路径中,有没有相加等于目标数的路径即可。但如果按照这个思路来,我们就使用多了一个参数,要接收路径走到这累计的和,这样的话就要写多一个函数了。
那我们能不能想出一个,不用多加参数,也能知道到底路径值相加等不等于目标数的方法呢?肯定有,相加来确定相不相等不方便,那我们就相减嘛,
路径值相加 = 目标数 等于 路径值相加 - 目标数 = 0
那思路就明确了,左右子树分开找,一直判断到叶子结点,看叶子节点的值跟传进来的值相减等不等于0。
var hasPathSum = function(root, targetSum) {
if(!root) return false
if(root.left == null && root.right == null) {
return targetSum-root.val == 0
}
return hasPathSum(root.left, targetSum-root.val) || hasPathSum(root.right, targetSum-root.val)
};
路径总和Ⅱ
上个路径总和是判断有没有符合的路径,而这个路径总和是找出路径。例如找22
输出[[5,4,11,2],[5,8,4,5]]
var pathSum = function (root, targetSum) {
const path = []
const list = []
const findPath = (node, sum) => {
if (node === null) return
list.push(node.val)
if (sum - node.val === 0 && node.right === null && node.left === null) {
path.push(list.slice())
} else {
findPath(node.left, sum - node.val)
findPath(node.right, sum - node.val)
}
list.pop()
}
findPath(root, targetSum)
return path
};
这一题我想了很久,不过是卡在了如何在递归遍历的时候写出路径,像平常那样打印的话,是会把递归的全部路径打印出来,我想的是往后退一步的时候,比如说7-11-2,在到2的时候能把已经压进path里面的7先pop()出来。
归根到底还是因为我对递归遍历的时候的执行过程还是不熟练,所以卡了很久list.pop()不知道应该放哪里好。尝试了放在不同的位置,然后的出来正确答案,但我就是没能想象出为什么放那里就行。下面是想通以后的一个简单模拟。
if (!root) return;
console.log(val)
findPath(root.left, sum - root.val) //1
findPath(root.right, sum - root.val) //2
path.pop() //3
其实我认为递归遍历二叉树,就是不断地把根节点的左/右子树压进去,一直压到底,然后开始释放往回走。
从上面的图举例,首先代码1一直找左(find...(root.left)),所以按5-4-11-7的节点路径一直压到栈里面。
- 代码1执行到节点7就到底了,在7节点再找左找右都没有了,执行代码3打印出7。
- 路径5-4-11-7就变成了5-4-11。然后开始往回走,回到节点11。
- 来到节点11时已经是找左然后从7中退出来了,现在到找右,找到节点2。
- 节点2找左找右都无效,所以执行代码3打印2后退出来,又回到节点11。
- 节点11已经执行完找左找右,又执行代码3打印11,退出来。
- 回到节点4,节点4刚刚找完左从11节点退出来,接下来执行找右,无果,执行代码3打印4......
这就是这里面发生的过程。所以list.pop放在13行的意思就是:这个路径深入到尾了,左右都找过了(10,11行),开始往回走了,所以就要pop出来了。