<p align="center">
<a href="https://programmercarl.com/other/kstar.html" target="_blank">
  <img src="https://code-thinking-1253855093.file.myqcloud.com/pics/20210924105952.png" width="1000"/>
</a>
<p align="center"><strong><a href="https://mp.weixin.qq.com/s/tqCxrMEU-ajQumL1i8im9A">参与本项目</a>，贡献其他语言版本的代码，拥抱开源，让更多学习算法的小伙伴们收益！</strong></p>
# 129. 求根节点到叶节点数字之和

[力扣题目链接](https://leetcode-cn.com/problems/sum-root-to-leaf-numbers/)

# 思路

本题和[113.路径总和II](https://programmercarl.com/0112.路径总和.html#_113-路径总和ii)是类似的思路，做完这道题，可以顺便把[113.路径总和II](https://programmercarl.com/0112.路径总和.html#_113-路径总和ii) 和 [112.路径总和](https://programmercarl.com/0112.路径总和.html#_112-路径总和) 做了。

结合112.路径总和 和 113.路径总和II，我在讲了[二叉树：递归函数究竟什么时候需要返回值，什么时候不要返回值？](https://programmercarl.com/0112.路径总和.html)，如果大家对二叉树递归函数什么时候需要返回值很迷茫，可以看一下。

接下来在看本题，就简单多了，本题其实需要使用回溯，但一些同学可能都不知道自己用了回溯，在[二叉树：以为使用了递归，其实还隐藏着回溯](https://programmercarl.com/二叉树中递归带着回溯.html)中，我详细讲解了二叉树的递归中，如何使用了回溯。

接下来我们来看题：

首先思路很明确，就是要遍历整个树把更节点到叶子节点组成的数字相加。

那么先按递归三部曲来分析：

## 递归三部曲

如果对递归三部曲不了解的话，可以看这里：[二叉树：前中后递归详解](https://programmercarl.com/二叉树的递归遍历.html)

* 确定递归函数返回值及其参数

这里我们要遍历整个二叉树，且需要要返回值做逻辑处理，所有返回值为void，在[二叉树：递归函数究竟什么时候需要返回值，什么时候不要返回值？](https://programmercarl.com/0112.路径总和.html)中，详细讲解了返回值问题。

参数只需要把根节点传入，此时还需要定义两个全局遍历，一个是result，记录最终结果，一个是vector<int> path。

**为什么用vector类型（就是数组）呢？ 因为用vector方便我们做回溯！** 

所以代码如下：

```
int result;
vector<int> path;
void traversal(TreeNode* cur) 
```

* 确定终止条件 

递归什么时候终止呢？ 

当然是遇到叶子节点，此时要收集结果了，通知返回本层递归，因为单条路径的结果使用vector，我们需要一个函数vectorToInt把vector转成int。

终止条件代码如下：

```
if (!cur->left && !cur->right) { // 遇到了叶子节点
    result += vectorToInt(path);
    return;
}
```

这里vectorToInt函数就是把数组转成int，代码如下：

```CPP
int vectorToInt(const vector<int>& vec) {
    int sum = 0;
    for (int i = 0; i < vec.size(); i++) {
        sum = sum * 10 + vec[i];
    }
    return sum;
}
```


* 确定递归单层逻辑

本题其实采用前中后序都不无所谓， 因为也没有中间几点的处理逻辑。

这里主要是当左节点不为空，path收集路径，并递归左孩子，右节点同理。

**但别忘了回溯**。

如图：


<img src='https://code-thinking.cdn.bcebos.com/pics/129.求根到叶子节点数字之和.png' width=600> </img></div>

代码如下：

```CPP
                 // 中
if (cur->left) { // 左 （空节点不遍历）
    path.push_back(cur->left->val);
    traversal(cur->left);    // 递归
    path.pop_back();         // 回溯
}
if (cur->right) { // 右 （空节点不遍历）
    path.push_back(cur->right->val);
    traversal(cur->right);   // 递归
    path.pop_back();         // 回溯
}
```

这里要注意回溯和递归要永远在一起，一个递归，对应一个回溯，是一对一的关系，有的同学写成如下代码：

```CPP
if (cur->left) { // 左 （空节点不遍历）
    path.push_back(cur->left->val);
    traversal(cur->left);    // 递归
}
if (cur->right) { // 右 （空节点不遍历）
    path.push_back(cur->right->val);
    traversal(cur->right);   // 递归
}
path.pop_back();         // 回溯
```
**把回溯放在花括号外面了，世界上最遥远的距离，是你在花括号里，而我在花括号外！** 这就不对了。

## 整体C++代码

关键逻辑分析完了，整体C++代码如下：

```CPP
class Solution {
private:
    int result;
    vector<int> path;
    // 把vector转化为int
    int vectorToInt(const vector<int>& vec) {
        int sum = 0;
        for (int i = 0; i < vec.size(); i++) {
            sum = sum * 10 + vec[i];
        }
        return sum;
    }
    void traversal(TreeNode* cur) {
        if (!cur->left && !cur->right) { // 遇到了叶子节点
            result += vectorToInt(path);
            return;
        }

        if (cur->left) { // 左 （空节点不遍历）
            path.push_back(cur->left->val);     // 处理节点
            traversal(cur->left);               // 递归
            path.pop_back();                    // 回溯，撤销
        }
        if (cur->right) { // 右 （空节点不遍历）
            path.push_back(cur->right->val);    // 处理节点
            traversal(cur->right);              // 递归
            path.pop_back();                    // 回溯，撤销
        }
        return ;
    }
public:
    int sumNumbers(TreeNode* root) {
        path.clear();
        if (root == nullptr) return 0;
        path.push_back(root->val);
        traversal(root);
        return result;
    }
};
```

# 总结

过于简洁的代码，很容易让初学者忽视了本题中回溯的精髓，甚至作者本身都没有想清楚自己用了回溯。

**我这里提供的代码把整个回溯过程充分体现出来，希望可以帮助大家看的明明白白！**


# 其他语言版本

Java： 

```java
class Solution {
    List<Integer> path = new ArrayList<>();
    int res = 0;
    public int sumNumbers(TreeNode root) {
        // 如果节点为0，那么就返回0
        if (root == null) return 0;
        // 首先将根节点放到集合中
        path.add(root.val);
        // 开始递归
        recur(root);
        return res;
    }

    public void recur(TreeNode root){
        if (root.left == null && root.right == null) {
            // 当是叶子节点的时候，开始处理
            res += listToInt(path);
            return;
        }

        if (root.left != null){
            // 注意有回溯
            path.add(root.left.val);
            recur(root.left);
            path.remove(path.size() - 1);
        }
        if (root.right != null){
            // 注意有回溯
            path.add(root.right.val);
            recur(root.right);
            path.remove(path.size() - 1);
        }
        return;
    }
    public int listToInt(List<Integer> path){
        int sum = 0;
        for (Integer num:path){
            // sum * 10 表示进位
            sum = sum * 10 + num;
        }
        return sum;
    }
}
```

Python：
```python3
class Solution:
    def sumNumbers(self, root: TreeNode) -> int:
        res = 0
        path = []
        def backtrace(root):
            nonlocal res
            if not root: return # 节点空则返回
            path.append(root.val)
            if not root.left and not root.right: # 遇到了叶子节点
                res += get_sum(path)
            if root.left: # 左子树不空
                backtrace(root.left)
            if root.right: # 右子树不空
                backtrace(root.right)
            path.pop()

        def get_sum(arr):
            s = 0
            for i in range(len(arr)):
                s = s * 10 + arr[i]
            return s

        backtrace(root)
        return res
```
Go：

JavaScript：
```javascript
var sumNumbers = function(root) {
    const listToInt = path => {
        let sum = 0;
        for(let num of path){
            // sum * 10 表示进位
            sum = sum * 10 + num;
        }
        return sum;
    }
    const recur = root =>{
        if (root.left == null && root.right == null) {
            // 当是叶子节点的时候，开始处理
            res += listToInt(path);
            return;
        }

        if (root.left != null){
            // 注意有回溯
            path.push(root.left.val);
            recur(root.left);
            path.pop();
        }
        if (root.right != null){
            // 注意有回溯
            path.push(root.right.val);
            recur(root.right);
            path.pop();
        }
        return;
    };
    const path = new Array();
    let res = 0;
    // 如果节点为0，那么就返回0
    if (root == null) return 0;
    // 首先将根节点放到集合中
    path.push(root.val);
    // 开始递归
    recur(root);
    return res;
};
```



-----------------------
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码一.jpg width=500> </img></div>
