路径总和

46 阅读3分钟

路径总和Ⅰ

题目:给你二叉树的根节点 root 和一个表示目标和的整数 targetSum ,判断该树中是否存在 根节点到叶子节点 的路径,这条路径上所有节点值相加等于目标和 targetSum 。

c8c1bdd979321a3e5e370821cfc7a9e.png 叶子节点:是指没有子节点的节点。

解析:要找到满足要求的路径,必须要满足

  • 根节点到叶子节点(叶子节点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

0f13f1da5c7c1b51cb9170ed35038fb.png

输出[[5,4,11,2],[5,8,4,5]]

var pathSum = function (root, targetSum) {
    const path = []
    const list = []
    const findPath = (node, sum) => {
        if (node === nullreturn
        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

其实我认为递归遍历二叉树,就是不断地把根节点的左/右子树压进去,一直压到底,然后开始释放往回走。

image.png

从上面的图举例,首先代码1一直找左(find...(root.left)),所以按5-4-11-7的节点路径一直压到栈里面。

  1. 代码1执行到节点7就到底了,在7节点再找左找右都没有了,执行代码3打印出7。
  2. 路径5-4-11-7就变成了5-4-11。然后开始往回走,回到节点11。
  3. 来到节点11时已经是找左然后从7中退出来了,现在到找右,找到节点2。
  4. 节点2找左找右都无效,所以执行代码3打印2后退出来,又回到节点11。
  5. 节点11已经执行完找左找右,又执行代码3打印11,退出来。
  6. 回到节点4,节点4刚刚找完左从11节点退出来,接下来执行找右,无果,执行代码3打印4......

这就是这里面发生的过程。所以list.pop放在13行的意思就是:这个路径深入到尾了,左右都找过了(10,11行),开始往回走了,所以就要pop出来了。