题目描述:给你二叉树的根节点 root 和一个整数目标和 targetSum ,找出所有 从根节点到叶子节点 路径总和等于给定目标和的路径。
叶子节点 是指没有子节点的节点。
方法一:深度优先搜索
思路及算法
我们可以采用深度优先搜索的方式,枚举每一条从根节点到叶子节点的路径。当我们遍历到叶子节点,且此时路径和恰为目标和时,我们就找到了一条满足条件的路径。
解题思路:
本问题是典型的二叉树方案搜索问题,使用回溯法解决,其包含 先序遍历 + 路径记录 两部分。
先序遍历: 按照 “根、左、右” 的顺序,遍历树的所有节点。
路径记录: 在先序遍历中,记录从根节点到当前节点的路径。当路径为 ① 根节点到叶节点形成的路径 且 ② 各节点值的和等于目标值 sum 时,将此路径加入结果列表。
算法流程:
pathSum(root, sum) 函数:
1、初始化: 结果列表 res ,路径列表 path 。
2、返回值: 返回 res 即可。
recur(root, tar) 函数:
1、递推参数: 当前节点 root ,当前目标值 tar 。
2、终止条件: 若节点 root 为空,则直接返回。
3、递推工作:
(1)路径更新: 将当前节点值 root.val 加入路径 path ;
(2)目标值更新: tar = tar - root.val(即目标值 tar 从 sum 减至 00 );
(3)路径记录: 当 ① root 为叶节点 且 ② 路径和等于目标值 ,则将此路径 path 加入 res 。
(4)先序遍历: 递归左 / 右子节点。
(5)路径恢复: 向上回溯前,需要将当前节点从路径 path 中删除,即执行 path.pop() 。
class Solution {
public:
vector<vector<int>> res;
vector<int> path;
vector<vector<int>> pathSum(TreeNode* root, int target)
{
recur(root,target);
return res;
}
void recur(TreeNode* root, int target)
{
// 节点为null
if(root == nullptr) return;
path.push_back(root->val);
target -= root->val;
// 到达叶子节点,且符合要求
if(target == 0 && root->left == nullptr && root->right == nullptr)
{
res.push_back(path);
//path.pop_back(); // 注意需要pop, 也可以不写接下来的两行,直接往下走即可。
//return;
}
recur(root->left, target);
recur(root->right, target);
path.pop_back(); // 注意需要pop
}
};
class Solution {
public:
vector<vector<int>> pathSum(TreeNode* root, int target)
{
vector<vector<int>> res;
vector<int> temp;
fun(root,target,res,temp);
return res;
}
void fun(TreeNode* root, int target, vector<vector<int>>& res, vector<int>& path)
{
// 节点为null
if(root == nullptr) return;
int val = root->val;
path.push_back(val);
// 到达叶子节点,且符合要求
if(target == root->val && root->left == nullptr && root->right == nullptr)
{
res.push_back(path);
// path.pop_back(); // 注意需要pop, 也可以不写接下来的两行,直接往下走即可。
//return;
}
fun(root->left, target - val, res, path);
fun(root->right, target - val, res, path);
path.pop_back(); // 注意需要pop
}
};
方法二:广度优先搜索
思路及算法
我们也可以采用广度优先搜索的方式,遍历这棵树。当我们遍历到叶子节点,且此时路径和恰为目标和时,我们就找到了一条满足条件的路径。
为了节省空间,我们使用哈希表记录树中的每一个节点的父节点。每次找到一个满足条件的节点,我们就从该节点出发不断向父节点迭代,即可还原出从根节点到当前节点的路径。
class Solution {
public:
vector<vector<int>> ret;
unordered_map<TreeNode*, TreeNode*> parent; //关联子节点和父节点
/*路径输出函数
*从左右子树为空的结点开始,循环向上找父节点
*并且将路过的结点值加入列表中
*/
void getPath(TreeNode* node)
{
vector<int> tmp;
while(node != nullptr)
{
tmp.emplace_back(node->val);
node = parent[node]; //迭代当前节点的父节点
}
reverse(tmp.begin(),tmp.end()); //因为从子节点向上遍历,所以调转列表顺序
ret.emplace_back(tmp);
}
vector<vector<int>> pathSum(TreeNode* root, int target)
{
if(root == nullptr) //判空
return ret;
queue<TreeNode*> q_node; //存结点队列,从上至下层序遍历每个节点
queue<int> q_sum; //存路径和队列
q_node.emplace(root); //根节点入队
q_sum.emplace(0);
while(!q_node.empty()){
TreeNode* node = q_node.front();
q_node.pop();
int rec = q_sum.front() + node->val; //将广度优先搜索到的结点的值存入rec中
q_sum.pop();
/*
*只有node为叶子结点且这一路结点和等于目标值
*才运行路径输出函数
*/
if(node->left == nullptr && node->right == nullptr)
{
if(rec == target)
getPath(node);
}
/*
*否则左子树不为空
*将左子树结点加入结点队列
*此结点为子树结点的父节点
*此条路径和加入路径队列中
*右子树同理
*/
else{
if(node->left!=nullptr){
q_node.emplace(node->left);
parent[node->left] = node;
q_sum.emplace(rec);
}
if(node->right!=nullptr){
q_node.emplace(node->right);
parent[node->right] = node;
q_sum.emplace(rec);
}
}
}
return ret;
}
};