第一次看到"1022从根到叶的二进制数之和"这个题目时,我脑海中立刻浮现出二叉树的结构。这是一道典型的二叉树遍历问题,但结合了二进制数的特性。题目要求我们计算从根节点到每个叶子节点的路径所表示的二进制数的总和。
举个例子,如果有这样一棵简单的二叉树:
code复制 1
/ \
0 1
那么从根到左叶子的路径是1->0,对应的二进制数是"10"(十进制2);根到右叶子的路径是1->1,对应"11"(十进制3)。所以总和就是2+3=5。
解决这个问题首先需要理解二叉树的遍历方式。常见的有三种深度优先遍历:
对于这个问题,前序遍历是最合适的,因为我们需要从根节点开始构建二进制数。
在遍历过程中,每深入一层,相当于二进制数左移一位(乘以2),然后加上当前节点的值。用数学表达式表示就是:
current_num = (parent_num << 1) | node.val
这里用位运算代替乘法和加法,效率更高。例如:
这类问题通常有两种实现方式:
我们先从递归方案开始,因为它更直观。
python复制def sumRootToLeaf(root):
def dfs(node, current_sum):
if not node:
return 0
current_sum = (current_sum << 1) | node.val
if not node.left and not node.right: # 叶子节点
return current_sum
return dfs(node.left, current_sum) + dfs(node.right, current_sum)
return dfs(root, 0)
让我们用之前的例子走一遍流程:
python复制def sumRootToLeaf(root):
if not root:
return 0
total = 0
stack = [(root, 0)]
while stack:
node, current_sum = stack.pop()
current_sum = (current_sum << 1) | node.val
if not node.left and not node.right:
total += current_sum
else:
if node.right:
stack.append((node.right, current_sum))
if node.left:
stack.append((node.left, current_sum))
return total
同样用之前的例子:
python复制if not root:
return 0
只有根节点时,直接返回根节点的值
题目保证结果在32位整数范围内,但实际工程中可能需要考虑:
我们已经使用了位运算来替代乘法和加法:
python复制current_sum = (current_sum << 1) | node.val
这比直接写:
python复制current_sum = current_sum * 2 + node.val
通常效率更高。
如果题目有特殊条件(如找到特定和就停止),可以提前终止遍历。
对于非常大的树,可以考虑并行处理左右子树。
python复制# 示例1
# 1
# / \
# 0 1
# 应返回5
# 示例2
# 1
# / \
# 0 1
# / \
# 0 1
# 应返回13 (100->4, 101->5, 11->3, 4+5+3=12? Wait...)
# 更正:路径为100(4),101(5),11(3) → 4+5+3=12
# 看来我的计算有误,应该是100(4),101(5),11(3) → 4+5+3=12
如果把二进制改为十进制,只需修改计算方式:
python复制current_sum = current_sum * 10 + node.val
不求总和,而是找最大的路径值:
python复制max_sum = max(max_sum, current_sum)
不仅要求和,还要记录所有路径:
python复制paths = []
def dfs(node, path):
if not node:
return
path.append(str(node.val))
if not node.left and not node.right:
paths.append(''.join(path))
dfs(node.left, path)
dfs(node.right, path)
path.pop()
这种类型的题目在实际中有多种应用:
python复制# 错误写法
def dfs(node, current_sum):
current_sum = (current_sum << 1) | node.val # 可能node为None
# 正确应该先检查node是否为None
python复制# 错误写法
if not node.left or not node.right: # 这会漏判只有一个子节点的情况
# 正确应该是
if not node.left and not node.right:
在迭代法中,容易忘记累加total:
python复制# 错误写法
if not node.left and not node.right:
return current_sum # 这会提前返回
# 正确应该累加到total
java复制public int sumRootToLeaf(TreeNode root) {
return dfs(root, 0);
}
private int dfs(TreeNode node, int sum) {
if (node == null) return 0;
sum = (sum << 1) | node.val;
if (node.left == null && node.right == null) {
return sum;
}
return dfs(node.left, sum) + dfs(node.right, sum);
}
cpp复制int sumRootToLeaf(TreeNode* root) {
return dfs(root, 0);
}
int dfs(TreeNode* node, int sum) {
if (!node) return 0;
sum = (sum << 1) | node->val;
if (!node->left && !node->right) {
return sum;
}
return dfs(node->left, sum) + dfs(node->right, sum);
}
在实际应用中:
根据具体场景选择合适的实现方式。
对于二叉树问题,可视化非常重要:
例如添加调试打印:
python复制def dfs(node, current_sum, path=[]):
if not node:
return 0
path.append(str(node.val))
current_sum = (current_sum << 1) | node.val
if not node.left and not node.right:
print(f"Path: {'->'.join(path)}, Value: {current_sum}")
path.pop()
return current_sum
res = dfs(node.left, current_sum, path) + dfs(node.right, current_sum, path)
path.pop()
return res
这个问题背后其实涉及:
理解这些可以帮助解决更复杂的问题。
在实际工程中:
要掌握这类问题,建议:
如果在面试中遇到:
类似题目推荐:
在实际实现中,我发现:
对于特别大的树:
这些在工程实践中可能更有意义。