题目难度: 中等
原题链接
今天继续更新 Leetcode 的剑指 Offer(专项突击版)系列, 大家在公众号 算法精选 里回复
剑指offer2
就能看到该系列当前连载的所有文章了, 记得关注哦~
题目描述
给定一个二叉树的根节点 root ,树中每个节点都存放有一个 0 到 9 之间的数字。
每条从根节点到叶节点的路径都代表一个数字:
例如,从根节点到叶节点的路径 1 -> 2 -> 3 表示数字 123 。
计算从根节点到叶节点生成的 所有数字之和 。
叶节点 是指没有子节点的节点。
示例 1:
- 输入:root = [1,2,3]
- 输出:25
- 解释:
- 从根到叶子节点路径 1->2 代表数字 12
- 从根到叶子节点路径 1->3 代表数字 13
- 因此,数字总和 = 12 + 13 = 25
示例 2:
- 输入:root = [4,9,0,5,1]
- 输出:1026
- 解释:
- 从根到叶子节点路径 4->9->5 代表数字 495
- 从根到叶子节点路径 4->9->1 代表数字 491
- 从根到叶子节点路径 4->0 代表数字 40
- 因此,数字总和 = 495 + 491 + 40 = 1026
提示:
- 树中节点的数目在范围 [1, 1000] 内
- 0 <= Node.val <= 9
- 树的深度不超过 10
题目思考
- 如何快速计算每条路径的数字和?
解决方案
思路
- 分析题目, 要想计算根节点到某个叶子节点的数字和, 我们需要知道该路径的每个节点对应的数字分别是多少, 然后将这些数字拼接起来, 就是当前路径的数字和
- 根据树的性质, 从根节点到任一节点一定只有唯一一条路径, 所以我们可以保存每一个节点对应路径的所有数字列表, 这样在求根节点到某个叶子的数字和时, 我们只需要在该叶子的父节点的数字列表基础上, 加上该叶子的数字即可
- 对于这个过程, 我们可以使用自顶向下的 DFS 来解决, 传入当前节点以及对应的
根节点->该节点
的沿途数字列表, 然后递归调用子节点, 传入子节点以及加上子节点数字的新列表, 直到到达叶子节点, 此时就可以将这些数字拼接起来, 并累加到最终结果里 - 不过这样我们就得维护当前路径的数字列表, 还得在到达叶子节点后拼接并求和, 有没有更快速的方法呢?
- 假设有路径
1->2->3->4
, 其对应的数字自然是 1234, 上面的做法是节点 1 对应数字列表[1]
, 节点 2 对应数字列表[1,2]
, 依此类推 - 我们其实完全没必要保存该列表, 而是可以直接保存数字本身, 然后在调用子节点时, 将该数字乘以 10, 并加上子节点的数字即可
- 也就是说, 节点 1 对应数字
1
, 节点 2 对应数字12=1*10+2
, 节点 3 对应数字123=12*10+3
, 节点 4 对应数字1234=123*10+4
, 这样就无需在到达叶子节点后拼接数字了 - 下面的代码就对应了上面的整个过程, 并且有详细的注释, 方便大家理解
复杂度
- 时间复杂度
O(N)
: 每个节点只需要遍历一次 - 空间复杂度
O(H)
: H 是树的深度, 也是递归栈的空间消耗
代码
class Solution:
def sumNumbers(self, root: TreeNode) -> int:
if not root:
# 根是空节点, 和为0
return 0
res = 0
def dfs(node, num):
# 传入当前节点node和"根节点->当前节点"的数字num
nonlocal res
if not node.left and not node.right:
# 当前节点是叶子节点, 累加其数字到最终结果
res += num
return
if node.left:
# 递归调用左子节点, 并更新数字
dfs(node.left, 10 * num + node.left.val)
if node.right:
# 递归调用右子节点, 并更新数字
dfs(node.right, 10 * num + node.right.val)
dfs(root, root.val)
return res
大家可以在下面这些地方找到我~😊
我的 GitHub
我的 Leetcode
我的 CSDN
我的知乎专栏
我的头条号
我的牛客网博客
我的公众号: 算法精选, 欢迎大家扫码关注~😊