【路飞】路径总和

188 阅读2分钟

记录 1 道算法题

路径总和

leetcode-cn.com/problems/pa…


要求:

    * 从根节点到叶子节点的某一条路径
    * val的总和等于给定的数则为正确路径,反之错误

这道题有两种解法,一个是广度,一个是深度。

无论是哪种都要先做是否叶子节点的判断,不是叶子节点即使数对了也算错。

广度是指把所有节点都做收集的动作,根据累加的方法,比较在某个节点时累加的数是不是等于给定的数。

      1
    2   7
  4  3    5
  
  给定一个数 6
  
  当收集到 1 的节点的时候,累计是1,不是叶子节点
  然后继续到 2 节点的时候, 累计是上面的 1 加上自己的val 2,同样不是叶子节点
  然后继续到 7 节点, 累计是 上面的 1 加上自己的val 7,不是叶子节点
  继续到 4 节点的时候, 累计是上面的 3 加自己的 4,是叶子节点。 7 !== 6, 所以不对
  然后到 3 节点, 累计是 上面的 3 加上自己的 3, 是叶子节点 等于6,所以是这个路径是对的,返回。
  

可以看出, 广度是以节点为单位,一层层收集。知道某个节点的时候也就知道了目前累计到多少,然后传给他的 left right, 这样他的子节点也知道了目前累计到多少。

就是斐波那契数列的既视感。

    function hasPathSum(root, targetSum) {
        if (!root) return false
        
        // 准备数组 存放收集起来的节点,因为一个节点能够算出他的left和right
        // 所以肯定是收集的速度比检查的速度快
        // 因为不好处理第一个,所以先写死。
        const stack = [root]
        const values = [root.val]
        
        // 如果用数组的 shift 的话,因为新的在后面推入了,所以只能 shift
        // while(stack.length) {
        //     const node = stack.shift()
        //     const val = values.shift()
        // }
        
        // 不用shift的版本就是通过下标控制
        while(i < stack.length) {
            const node = stack[i]
            const val = values[i]
            
            i++
            // 叶子节点
            if (!node.left && !node.right) {
                if (val === targetSum) {
                    return true
                } else {
                    // 是叶子节点,但不符合给定的数
                    continue
                }
            }
            
            if(node.left) {
                stack.push(node.left)
                // val 是已经累加到当前节点的数
                values.push(node.left.val + val)
            }
            
            if (node.right) {
                stack.push(node.right)
                values.push(node.right.val + val)
            }   
        }
        
        return false
    }

深度是跟前序遍历一样的,先把某一条路走完,判断合不合理,然后不停回溯,走新路。直到找到或者没找到。用的是递减的方法。

说到前序遍历就是递归。

    function hasPathSum(root, targetSum) {
        if (!root) return false
        
        // 先计算当前节点是否符合
        targetSum -= root.val
        // 叶子节点时进行比较
        if (!root.left && !root.right) {
            return targetSum === 0
        }
        
        // 在一开始已经减过了。
        // 同一个父节点,所以 累计的数是一样的。
        // 用了个或,刚刚好如果前面返回了true就返回了,false就等于回溯然后走新路。跟前序遍历很像的。
        return hasPathSum(root.left, targetSum) ||
                hasPathSum(root.right, targetSum)
    }