题意
给你二叉树的根节点 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;
}
};