LeetCode 1022. 从根到叶的二进制数之和
题目描述
给定一棵二叉树,每个节点的值都是 0 或 1。每条从根到叶子的路径都代表一个从最高有效位开始的二进制数。例如,如果路径为 0 -> 1 -> 1,那么它表示的二进制数是 011,也就是十进制 3。
要求返回所有从根到叶子的路径所表示的二进制数之和。
示例
输入:[1,0,1,0,1,0,1]
输出:22
解释:所有根到叶子的路径及其对应的二进制数:
- 1->0->0: 100 (4)
- 1->0->1: 101 (5)
- 1->1->0: 110 (6)
- 1->1->1: 111 (7)
总和 = 4+5+6+7 = 22
算法思路
采用深度优先搜索遍历整棵树,在遍历过程中记录从根节点到当前节点的二进制值。
- 每当访问一个节点时,将当前累积的二进制值左移一位(相当于乘以 2),再加上当前节点的值,得到从根到该节点的二进制数。
- 如果当前节点是叶子节点(无左右孩子),则将这个值累加到答案中。
- 否则,继续递归遍历其左右子树,并将左右子树返回的和相加。
代码实现
cpp
class Solution {
public:
int dfs(TreeNode* root, int cur) {
// 更新当前路径的二进制值:左移一位再加当前节点值
cur = cur * 2 + root->val;
// 如果是叶子节点,直接返回当前路径的值
if (!root->left && !root->right) {
return cur;
}
int sum = 0;
// 递归左子树
if (root->left) {
sum += dfs(root->left, cur);
}
// 递归右子树
if (root->right) {
sum += dfs(root->right, cur);
}
return sum;
}
int sumRootToLeaf(TreeNode* root) {
return dfs(root, 0);
}
};
代码解释
- 主函数
sumRootToLeaf:从根节点开始 DFS,初始路径值cur = 0。 - DFS 函数 :
- 参数
root表示当前节点,cur表示从根到当前节点的父节点的二进制值。 - 首先计算到当前节点的路径值:
cur = cur * 2 + root->val。这是因为二进制数的生成规则:每深入一层,前面的位就要左移一位(乘以 2),然后加上新节点的值(0 或 1)。 - 如果是叶子节点,则返回当前路径值。
- 否则,递归处理左右子树,并将它们返回的和累加后返回。
- 参数
示例图解
以二叉树 [1,0,1] 为例:
1
/ \
0 1
- 从根节点 1 开始,
cur = 0*2 + 1 = 1,非叶子,递归左子树。 - 左子节点 0:
cur = 1*2 + 0 = 2,叶子节点,返回 2。 - 右子节点 1:
cur = 1*2 + 1 = 3,叶子节点,返回 3。 - 根节点将左右返回值相加:2 + 3 = 5,即路径
"10"=2和"11"=3的和。
常见错误分析
初学者容易写出类似下面的错误代码:
cpp
int sumRootToLeaf(TreeNode* root) {
if(!root) return 0;
int sum = 0;
sum += ((root -> val) << 1) + sumRootToLeaf(root -> left);
sum += ((root -> val) << 1) + sumRootToLeaf(root -> right);
return sum;
}
问题所在 :这种写法在递归调用 sumRootToLeaf(root->left) 时,返回的是以左子节点为根的子树中所有路径的二进制数之和,但这些路径的二进制值是从左子节点开始计算的,而不是从当前根节点开始。直接将其与 (root->val) << 1 相加,相当于错误地假设所有子路径长度均为 1,忽略了路径的实际长度,导致结果错误。
正确的做法必须将当前累积的二进制值通过参数向下传递,确保每条路径的每一位都正确累加。
复杂度分析
- 时间复杂度:O(N),其中 N 为二叉树的节点数。每个节点恰好被访问一次。
- 空间复杂度:O(H),H 为树的高度。递归调用栈的深度取决于树的形状,最坏情况下(树退化为链表)为 O(N),平均情况下为 O(log N)。
总结
本题是典型的二叉树 DFS 题目,核心在于如何在递归过程中正确维护从根到当前节点的路径二进制值。通过参数传递当前累积值,并在叶子节点处返回该值,可以简洁地解决问题。理解二进制数左移累加的过程,是正确解答的关键。