持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第2天,点击查看活动详情
给出一棵二叉树,其上每个结点的值都是 0 或 1 。每一条从根到叶的路径都代表一个从最高有效位开始的二进制数。
- 例如,如果路径为
0 -> 1 -> 1 -> 0 -> 1,那么它表示二进制数01101,也就是13。
对树上的每一片叶子,我们都要找出从根到该叶子的路径所表示的数字。
返回这些数字之和。题目数据保证答案是一个 32 位 整数。
示例 1:
输入:root = [1,0,1,0,1,0,1]
输出:22
解释:(100) + (101) + (110) + (111) = 4 + 5 + 6 + 7 = 22
示例 2:
输入:root = [0]
输出:0
提示:
- 树中的节点数在
[1, 1000]范围内 Node.val仅为0或1
方法一:递归
后序遍历的访问顺序为:左子树——右子树——根节点。我们对根节点 进行后序遍历:
- 如果节点是叶子节点,返回它对应的数字 。
- 如果节点是非叶子节点,返回它的左子树和右子树对应的结果之和。
var sumRootToLeaf = function(root) {
const dfs = (root, val) => {
if (!root) {
return 0;
}
val = (val << 1) | root.val;
if (!root.left&& !root.right) {
return val;
}
return dfs(root.left, val) + dfs(root.right, val);
}
return dfs(root, 0);
};
复杂度分析
- 时间复杂度:O(n),其中 n 是节点数目。总共访问 n 个节点。
- 空间复杂度:。递归栈需要 的空间。
方法二:迭代
我们用栈来模拟递归,同时使用一个 指针来记录先前访问的节点。算法步骤如下:
-
如果节点 非空,我们将不断地将它及它的左节点压入栈中。
-
我们从栈中获取节点:
- 该节点的右节点为空或者等于 ,说明该节点的左子树及右子树都已经被访问,我们将它出栈。如果该节点是叶子节点,我们将它对应的数字 加入结果中。设置 为该节点,设置 为空指针。
- 该节点的右节点非空且不等于 ,我们令 指向该节点的右节点。
-
如果 为空指针或者栈空,中止算法,否则重复步骤 1 。
需要注意的是,每次出入栈都需要更新 。
var sumRootToLeaf = function(root) {
const stack = [];
let val = 0, ret = 0;
let prev = null;
while (root || stack.length) {
while (root) {
val = (val << 1) | root.val;
stack.push(root);
root = root.left;
}
root = stack[stack.length - 1];
if (!root.right || root.right === prev) {
if (!root.left && !root.right) {
ret += val;
}
val >>= 1;
stack.pop();
prev = root;
root = null;
} else {
root = root.right;
}
}
return ret;
};
复杂度分析
- 时间复杂度:O(n),其中 n 是节点数目。总共访问 n 个节点。
- 空间复杂度:O(n)。栈最多压入 n 个节点。