题目要求计算根节点到所有叶节点的数字之和,因此需要遍历整个二叉树。
涉及二叉树的题目一般考虑用递归解决,涉及路径又可以考虑回溯法(DFS)。
其实递归和回溯不能完全分开,因为递归中往往包含着回溯。
知识点: 递归、回溯法(DFS)
1. 遍历框架
首先,先来回顾二叉树的遍历框架 ,具体可见我之前的文章 数据结构之树(一)。
这里以后序遍历为例,首先需要判断结点是否为空,然后递归左右子树。
void PostOrderTraversal(BinTree BT)
{
if(BT) // 为空则返回
{
PostOrderTraversal(BT->left); // 递归左子树,BT->left左子树根节点地址
PostOrderTraversal(BT->right); // 递归右子树,BT->right右子树根节点指针
print("%d", BT->val); //访问根节点
}
}
下面介绍一种更为广义的多叉树遍历框架,来自 labuladong 的算法小抄
void traverse(TreeNode root) {
for (TreeNode child : root.childern)
// 前序遍历需要的操作
traverse(child);
// 后序遍历需要的操作
}
2. 思路
递归的终止条件:到达叶子结点时,将整条路径的数字加到最后的结果中,并返回
递归函数的作用: 求根节点的左子结点到叶结点的数字之和,求根节点的右子结点到叶结点的数字之和。
递归的参数和返回值:参数为当前路径,用 vector<int> path
来表示。回溯法的返回值一般为 void,故此处返回值为 void。此处还需声明全局变量 result 用于保存最终的数字。
3. 实现
下面以递归左子结点为例介绍递归函数的实现。
前序遍历代码:进行递归子节点前进行的操作,具体来说就是把左子节点的值加到 path 中
后序遍历代码:进行递归子节点后进行的操作,具体来说就是把左子节点的值从 path 中移除。其实,也是一个回溯的过程,恢复到根节点状态下的 path。
if(root->left)
{
path.push_back(root->left->val); # 前序遍历代码
traverse(root->left, path); # 递归(遍历)
path.pop_back(); # 后续遍历代码
}
回溯的过程如下图所示:
递归左结点:路径由 1 变为 12,
回溯:从 12 变为 1;
递归右结点:路径由 1 变为 13,
回溯:从 13 变为 1;
下面给出具体的代码实现:
法1:详细版
函数 int vectoint(vector<int> path)
将 path 转换为数字
class Solution {
public:
int result = 0;
int sumNumbers(TreeNode* root) {
vector<int> path;
# 如果根节点为空,返回数字0
if(root == nullptr) return 0;
# 向 path 中添加根节点值
path.push_back(root->val);
# 调用递归
traverse(root, path);
return result;
}
int vectoint(vector<int> path)
{
int sum = 0;
for(int i = 0; i < path.size(); i++)
{
sum = sum * 10 + path[i];
}
return sum;
}
void traverse(TreeNode* root, vector<int> path)
{
# 遇到叶子结点,将结果加入 path 中,返回
if(!root->left && !root->right)
{
result += vectoint(path);
return;
}
if(root->left)
{
path.push_back(root->left->val);
traverse(root->left, path);
path.pop_back();
}
if(root->right)
{
path.push_back(root->right->val);
traverse(root->right, path);
path.pop_back();
}
}
};
法2:简洁版
如果是叶子结点就直接返回结果,如果是非叶子结点就计算子节点数字的和。
pre 是先前路径的数字和,sum 是加入新的结点后的数字和。
class Solution {
public:
int sumNumbers(TreeNode* root) {
return traverse(root, 0);
}
int traverse(TreeNode* root, int pre)
{
# 如果 root 为空就返回 0,避免出现 root->val 访问空指针
if(root == nullptr) return 0;
# sum 为当前路径和,在 pre 的基础上加入新的结点值
int sum = pre * 10 + root->val;
if(!root->left && !root->right)
{
# 叶子结点直接返回
return sum;
}
else
{
return traverse(root->left, sum) + traverse(root->right, sum);
}
}
};