剑指 Offer 34. 二叉树中和为某一值的路径

题目介绍

描述:

输入一棵二叉树和一个整数,打印出二叉树中节点值的和为输入整数的所有路径。从树的根节点开始往下一直到叶节点所经过的节点形成一条路径。

示例:
给定如下二叉树,以及目标和 sum = 22,

              5
             / \\
            4   8
           /   / \\
          11  13  4
         /  \\    / \\
        7    2  5   1
返回:

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

提示:

节点总数 <= 10000

解题思路:

递归算法的关键是要明确函数的「定义」是什么,然后相信这个定义,利用这个定义推导最终结果。

写树相关的算法,简单说就是,先搞清楚当前 root 节点该做什么,然后根据函数定义递归调用子节点,递归调用会让孩子节点做相同的事情。

二叉树题目的一个难点在于如何通过题目的要求思考出每一个节点需要做什么

二叉树解题策略

一 递归 二 队列 + 迭代 (层次遍历) 三 栈 + 迭代 (非递归遍历) 四 其它

三种基本的遍历方式,都可以用递归来实现。写递归算法的时候,需要注意递归退出条件以及递归操作的表达。

自己的解法实现

def pathSum4(self, root, sum):
        if not root: return []
        res, stack = [], []

        def getPath(node, _sum):
            stack.append(node.val)
            _sum += node.val
            if _sum == sum and not node.left and not node.right:
                res.append(list(stack))
            if node.left:
                getPath(node.left, _sum)
            if node.right:
                getPath(node.right, _sum)
            stack.pop(-1)

        getPath(root, 0)
        return res

网上比较优秀的解法

解法一

解题思路: 本问题是典型的二叉树方案搜索问题,使用回溯法解决,其包含 先序遍历 + 路径记录 两部分。

先序遍历: 按照 “根、左、右” 的顺序,遍历树的所有节点。 路径记录: 在先序遍历中,记录从根节点到当前节点的路径。当路径为 ① 根节点到叶节点形成的路径 且 ② 各节点值的和等于目标值 sum 时,将此路径加入结果列表。

向上回溯前,需要将当前节点从路径 path 中删除,即执行 path.pop() 。

值得注意的是,记录路径时若直接执行 res.append(path) ,则是将 path 对象加入了 res ;后续 path 改变时, res 中的 path 对象也会随之改变。

正确做法:res.append(list(path)) ,相当于复制了一个 path 并加入到 res 。

def pathSum(self, root, sum):
        if not root: return [[]]
        res, path = [], []
        def recur(node, target):
            if not node: return
            path.append(node.val)
            target -= node.val

            if target == 0 and not node.left and not node.right:
                res.append(list(path))
            recur(node.left, target)
            recur(node.right, target)
            path.pop()
        recur(root, sum)
        return res

解法二

使用DFS

def pathSum2(self, root, sum):
        if not root: return [[]]
        res, path = [], []
        def dfs(node, sum):
            # 递归出口:解决子问题
            if not node: return    # 如果没有节点(node = None),直接返回,不向下执行
            else:
                path.append(node.val)    # 将节点值添加到path
                sum -= node.val
            # 如果节点为叶子节点,并且 sum == 0
            if not sum and not node.left and not node.right:
                res.append(path[:])
            dfs(node.left, sum)    # 递归处理左边
            dfs(node.right, sum)   # 递归处理右边
            path.pop()    #处理完一个节点后,恢复初始状态,为node.left,  node.right操作
        dfs(root, sum)
        return res

解法三

探测二叉树的每个节点,沿途记录路径列表和累和,并判断:

节点是叶节点且累加和刚好满足要求,则沿途列表加入到结果列表 否则,继续对可能的左右子节点递归探测

def pathSum3(self, root, sum):
        if not root: return []
        res = []
        def path(node, sum_, _list):
            if not node.left and not node.right and sum_ == node.val:
                res.append(_list[:] + [node.val])
            if node.left:
                path(node.left, sum_ - node.val, _list + [node.val])
            if node.right:
                path(node.right, sum_ - node.val, _list + [node.val])
        path(root, sum, [])
        return res

相关知识总结和思考

相关知识:

BFS:广度/宽度优先。其实就是从上到下,先把每一层遍历完之后再遍历一下一层。

可以使用Queue的数据结构。我们将root节点初始化进队列,通过消耗尾部,插入头部的方式来完成BFS。

二叉搜索树(BST)的特性:

  1. 若它的左子树不为空,则所有左子树上的值均小于其根节点的值
  2. 若它的右子树不为空,则所有右子树上的值均大于其根节点的值
  3. 它的左右子树也分别为二叉搜索树

递归与迭代的区别

递归:重复调用函数自身实现循环称为递归; 迭代:利用变量的原值推出新值称为迭代,或者说迭代是函数内某段代码实现循环;

你可能感兴趣的:(剑指 Offer 34. 二叉树中和为某一值的路径)