剑指Offer 34—二叉树中和为某一值的路径

力扣

题意

给你二叉树的根节点 root 和一个整数目标和 targetSum ,找出所有 从根节点到叶子节点 路径总和等于给定目标和的路径。

叶子节点:是指没有子节点的节点。

 

法1—深度优先搜索DFS

本题的要求是,找到所有满足从「根节点」到某个「叶子节点」经过的路径上的节点之和等于目标和的路径。核心思想是对树进行一次遍历,在遍历时记录从根节点到当前节点的路径和,以防止重复计算。

我们可以采用深度优先搜索的方式,枚举每一条从根节点到叶子节点的路径

当我们遍历到叶子节点,且此时路径和恰为目标和时,我们就找到了一条满足条件的路径。

当我们遍历到叶子节点,但此时路径和不为目标和时,就要从 记录根节点到该叶子节点的变量path中 删除该叶子节点,并且回溯至该叶子节点的父节点,进行下一个路径的遍历。

C++实现

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode() : val(0), left(nullptr), right(nullptr) {}
 *     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
 *     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
 * };
 */
class Solution 
{
public:
     vector<vector<int>> res;    //记录最终的结果
    vector<int> path;   //记录根节点到叶子节点
public:
    vector<vector<int>> pathSum(TreeNode* root, int target) 
    {
        if(nullptr==root)
            return {};
        dfs(root,target);
        return res;   
    }   
    void dfs(TreeNode* root,int target)
    {
        // 空节点直接返回
        if(nullptr==root)
            return;

        //将本节点的值加入path中
        //emplace_back:在容器尾部直接生成一个元素。该函数和 push_back() 的功能相同,但效率更高。
        /*
        push_back() 向容器尾部添加元素时,首先会创建这个元素,然后再将这个元素拷贝或者移动到容器
        (如果是拷贝的话,事后会自行销毁先前创建的这个元素);
        而 emplace_back() 在实现时,则是直接在容器尾部创建这个元素,省去了拷贝或移动元素的过程。
        */
        path.emplace_back(root->val);

        //target要减掉本节点的值
        target-=root->val;

        //判断当前节点是不是叶子节点,并且从根节点到该叶子节点的值是否满足和等于target
        if(root->left==nullptr&&root->right==nullptr&&target==0)
        {
            res.emplace_back(path);
        }
        dfs(root->left,target);//先一直往左走,一直走到最左边的叶子节点
        dfs(root->right,target);
        path.pop_back();    //从path中删除本节点,回溯到上一层
    }
};

法2—广度优先遍历

我们也可以采用广度优先搜索的方式,遍历这棵树。当我们遍历到叶子节点,且此时路径和恰为目标和时,我们就找到了一条满足条件的路径。

为了节省空间,我们使用哈希表(unordered_map无序容器)记录树中的每一个节点的父节点。每次找到一个满足条件的节点,我们就从该节点出发不断向父节点迭代,即可还原出从根节点到当前节点的路径。

C++实现

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode() : val(0), left(nullptr), right(nullptr) {}
 *     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
 *     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
 * };
 */
class Solution 
{
public:
    vector<vector<int>> res;    //记录最终的结果
    unordered_map<TreeNode*,TreeNode*> parent;  //记录节点的父节点是谁
public:

    //记录路径的函数,记录从叶子节点node到根节点所经过的路径
    void get_path(TreeNode* node) 
    {
        vector<int> path;
        while(node!=nullptr)
        {
            //将节点的值插入到path最前面,这样叶子节点最先插入path;到最后,叶子节点就在path中的最后位置,根节点在path中的最开头的位置。
            path.insert(path.begin(),node->val);

            //往上走
            node=parent[node];
        }
        //当跳出循环时,表示node==nullptr,即已经遍历到了根节点。
        //因为parent[根节点]为nullptr,根节点没有父节点。
        //对于unordered_map<key, value>:
        /*
        非键值访问,当value为指针类型时,返回nullptr
        非键值访问,当value为int类型时,返回0
        非键值访问,当value为char类型时,返回’\0’ 
        */

        res.emplace_back(path);  
    }   
    vector<vector<int>> pathSum(TreeNode* root, int target) 
    {
        // 空节点直接返回
        if(nullptr==root)
            return {};
        queue<TreeNode*> que;//记录节点,广度优先遍历借助队列
        queue<int> path_sum;//记录根节点的当前节点的值
        que.push(root);
        path_sum.push(0);
        while(!que.empty())
        {
            TreeNode* node=que.front();
            que.pop();
            int sum = path_sum.front()+node->val;   
            path_sum.pop(); 

            //如果本节点是叶子节点,并且根节点到本叶子节点路径和等于目标值
            if(node->left==nullptr&&node->right==nullptr&&sum==target)
            {
                get_path(node);
            }

            //否则。本节点的左节点不为空的话,就将左节点加入队列,并且将根节点到本节点路径和加入    path_sum,还要记录左节点的父节点是本节点。
            //右子树同理
            else
            {
                if(node->left)
                {
                    que.push(node->left);
                    path_sum.push(sum);
                    parent[node->left]=node;
                }
                if(node->right)
                {
                    que.push(node->right);
                    path_sum.push(sum);
                    parent[node->right]=node;
                }
            }
        }
        return res;       
    }
};

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

心之所向便是光v

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值