【乱杀图解算法】112. 路径总和

64 阅读2分钟

题目描述:

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

叶子节点 是指没有子节点的节点。

image.png

输入: root = [5,4,8,11,null,13,4,7,2,null,null,null,1], targetSum = 22

输出: true

解释: 等于目标和的根节点到叶节点路径如上图所示。

我原来是这么写的:

/**
 * Definition for a binary tree node.
 * function TreeNode(val, left, right) {
 *     this.val = (val===undefined ? 0 : val)
 *     this.left = (left===undefined ? null : left)
 *     this.right = (right===undefined ? null : right)
 * }
 */
/**
 * @param {TreeNode} root
 * @param {number} targetSum
 * @return {boolean}
 */
var hasPathSum = function(root, targetSum) {
    let sum = 0;
    let res = [];
    if(!root){
        res.push(sum);
        sum = 0;
        return false;
    }
    // 用前序遍历
    let preDfs = function (root) {
        let nodeVal = root.val;
        sum += nodeVal;
        if(root.left) {
            preDfs(root.left);
        }
        if(root.right) {
            preDfs(root.right);
        }
    }
    preDfs(root);

    if(res.indexOf(targetSum) !== -1){
        return true;
    } else {
        return false;
    }
};

要获取路径总和,肯定少不了遍历,我采用的前序遍历。

我本来是想,遍历每一条路径,然后把每条路径上的节点加和,依次push到一个数组中,最后再遍历这个数组,如果数组里面有和targetSum相等的数,那就说明存在这样的路径满足总和为targetSum。

然而测试用例只通过了一半。。。加和比对的方式也比较费劲儿

看了 代码随想录 的思路之后,豁然开朗

用递减的思路来做这道题,会更简易一些,思路也比较好理解

/**
 * Definition for a binary tree node.
 * function TreeNode(val, left, right) {
 *     this.val = (val===undefined ? 0 : val)
 *     this.left = (left===undefined ? null : left)
 *     this.right = (right===undefined ? null : right)
 * }
 */
/**
 * @param {TreeNode} root
 * @param {number} targetSum
 * @return {boolean}
 */
var hasPathSum = function(root, targetSum) {
    if(!root)return false;
    let tranversal = (sum, node) => {
        // 递归的终止条件
        if(!node.left && !node.right && sum == 0)return true; // 叶子节点且满足targetSum
        if(!node.left && !node.right)return false;

        // 单层递归逻辑 and 递归终止条件
        if (node.left && tranversal(sum - node.left.val, node.left)) {
            // 需要传递节点
            return true;
        }
        if (node.right && tranversal(sum - node.right.val, node.right)) {
            return true;
        }
        return false;
    }
    return tranversal(targetSum - root.val, root);
};

需要注意的是,在 单层递归逻辑 中,有个比较不好理解的地方:

在单层递归的判断条件中,有个 tranversal(sum - node.left.val, node.left)这行代码其实是把上面的if(!node.left && !node.right && sum == 0)return true;这个return结果拿过来做判断了

意思是,存在node.left的同时,我们对这个左子节点做tranversal的计算,如果满足叶子节点且targetSum减到最后的结果也为0(即这个判断条件为true),那么就在下面的单层递归逻辑中返回true

这个tranversal里面的操作可以理解为一个回溯

借用代码随想录的图:

image.png

最后顺便总结一下,递归三部曲

  1. 确定递归要不要有返回值,如果有,返回啥,返回什么类型
  2. 确定递归的结束条件
  3. 确定单层递归的逻辑

🌸 🌸 🌸完结撒花 🌸 🌸 🌸