[牛客网-Leetcode] #树 中等 sum-root-to-leaf-numbers

根节点到叶节点路径和 sum-root-to-leaf-numbers

题目描述

给定一个仅包含数字0-9的二叉树,每一条从根节点到叶子节点的路径都可以用一个数字表示。
例如根节点到叶子节点的一条路径是1->2->3,那么这条路径就用123来代替。
找出根节点到叶子节点的所有路径表示的数字之和
例如:
1↵ / ↵ 2 3
根节点到叶子节点的路径1->2用数字12代替
根节点到叶子节点的路径1->3用数字13代替
所以答案为12+13=25

Given a binary tree containing digits from 0-9 only, each root-to-leaf path could represent a number.
An example is the root-to-leaf path1->2->3which represents the number123.
Find the total sum of all root-to-leaf numbers.
For example,
1↵ / ↵ 2 3
The root-to-leaf path1->2represents the number12.
The root-to-leaf path1->3represents the number13.
Return the sum = 12 + 13 =25.

示例

示例1
输入

{1,0}

输出

10

示例2
输入

{1,#,5}

输出

15

解题思路

思路1:BFS

  • 根据BFS找到所有叶子节点到根节点的路径,然后相加
  • 用mp记录所有 孩子->双亲 的映射,类似于并查集,这样就可以从叶节点一直遍历到根节点。
    • 注意: 不能建立双亲->孩子的映射,因为双亲可能有多个孩子,而映射必须是唯一的,所以只能是孩子到双亲一对一
  • 用leafNode数组保存所有的叶子节点,最后利用mp映射对每一个叶节点求其路径和,并累加
/**
 * struct TreeNode {
 *	int val;
 *	struct TreeNode *left;
 *	struct TreeNode *right;
 * };
 */
#include "unordered_map"
class Solution {
    
    
public:
    int sumNumbers(TreeNode* root) {
    
    
        if(root == NULL) return 0;
        int res(0);
        queue<TreeNode*> myque;
        myque.push(root);
        //用于保存从叶节点到根节点的路径
        unordered_map<TreeNode*, TreeNode*> mp;  
        mp[root] = root;
        //用于保存所有的叶节点,方便遍历mp
        vector<TreeNode*> leafNode;
        
        while(!myque.empty()) {
    
    
            int size = myque.size();
            for(int i = 0; i < size; i ++) {
    
    
                TreeNode* temp = myque.front();
                myque.pop();
                //如果左右子树均为空,则添加进leafnode中
                if(!temp -> left && !temp -> right) {
    
    
                    leafNode.push_back(temp);
                }
                //如果有左右孩子,则添加 孩子->双亲 的映射
                if(temp -> left) {
    
    
                    myque.push(temp -> left);
                    mp[temp -> left] = temp;
                }
                if(temp -> right) {
    
    
                    myque.push(temp -> right);
                    mp[temp -> right] = temp;
                }
            }
        }
        //对每一个叶节点都计算其路径和,并累加
        for(auto node : leafNode) {
    
    
            int sum(0);  //记录每一个叶节点的路径和
            int temp(1);
            while(node != root) {
    
    
                sum += node -> val * temp;
                temp *= 10;
                node = mp[node];  //不断往根节点遍历
            }
            sum += root -> val * temp;  //最后加上根节点
            res += sum;
        }
        
        return res;
    }
};

思路2:回溯

  • 路径:记录到track中
  • 选择列表:不空的左右子树
  • 结束条件:到达叶节点,则累加
  • 注意:回溯法解决树的路径问题之前,要先把根节点添加进路径
  • 补充,accumulate函数的用法:
    • accumulate的头文件为:#include <numeric>

    • 例如对于vector<int> vec数组,对其进行求和:
      int sum = accumulate(vec.begin(), vec.end(), 0)
      sum中保存了累加求和后的结果,前两个参数指定求和的范围,第三个参数指定累加的初值。

      也可以使用accumulate把string型的vector容器中的元素连接起来:
      string sum = accumulate(v.begin() , v.end() , string(" "));
      这个函数调用的效果是:从空字符串开始,把vec里的每个元素连接成一个字符串。

/**
 * struct TreeNode {
 *	int val;
 *	struct TreeNode *left;
 *	struct TreeNode *right;
 * };
 */
#include<numeric>
class Solution {
    
    
public:
    int sumNumbers(TreeNode* root) {
    
    
        if(root == NULL) return 0;
        int res(0);
        vector<int> track;
        
        //先把根节点添加进路径
        track.push_back(root -> val);
        backtrack(root, track, res);
        return res;
    }
    void backtrack(TreeNode* root, vector<int>& track, int& res) {
    
    
        if(root == NULL) return ;
        //叶子节点,则累加
        if(!root -> left && !root -> right) {
    
    
            int sum(0);  //对于每一个叶节点,记录其路径和
            for(int i = 0; i < track.size(); i ++) {
    
    
                sum = sum * 10 + track[i];
            }
            res += sum;
        }
        if(root -> left) {
    
    
            track.push_back(root -> left -> val);
            backtrack(root -> left, track, res);
            track.pop_back();
        }
        if(root -> right) {
    
    
            track.push_back(root -> right -> val);
            backtrack(root -> right, track, res);
            track.pop_back();
        }
    }
};

思路3:DFS

  • DFS和回溯的写法很类似,只不过DFS是直接把更新后的参数带入了下一层,回溯是先更新,再撤回,但二者的本质都是做选择
  • DFS和回溯一样,在面对树的路径和问题时,都要先把根节点添加进结果
/**
 * struct TreeNode {
 *	int val;
 *	struct TreeNode *left;
 *	struct TreeNode *right;
 * };
 */
#include<numeric>
class Solution {
    
    
public:
    int sumNumbers(TreeNode* root) {
    
    
        if(root == NULL) return 0;
        int res = 0; //用于保存最终结果
        int sum = root -> val;  //用于记录每个叶节点的路径和,初始为根节点的值
        dfs(root, res, sum);
        return res;
    }
    void dfs(TreeNode* root, int& res, int sum) {
    
    
        if(root == NULL) return ;
        //如果是叶节点,则累加
        if(!root -> left && !root -> right) {
    
    
            res += sum;
        }
        //两个选择:左子树和右子树
        if(root -> left) {
    
    
            dfs(root -> left, res, sum * 10 + root -> left -> val);
        }
        if(root -> right) {
    
    
            dfs(root -> right, res, sum * 10 + root -> right -> val);
        }
    }
};

猜你喜欢

转载自blog.csdn.net/cys975900334/article/details/106896402