题目13:113. 路径总和 II
同类题目
前置知识
- 无
题目描述
-
给你二叉树的根节点
root和一个整数目标和targetSum,找出所有 从根节点到叶子节点 路径总和等于给定目标和的路径。叶子节点 是指没有子节点的节点。
示例 1:
输入:root = [5,4,8,11,null,13,4,7,2,null,null,5,1], targetSum = 22 输出:[[5,4,11,2],[5,8,4,5]]示例 2:
输入:root = [1,2,3], targetSum = 5 输出:[]示例 3:
输入:root = [1,2], targetSum = 0 输出:[]提示:
- 树中节点总数在范围
[0, 5000]内 -1000 <= Node.val <= 1000-1000 <= targetSum <= 1000
- 树中节点总数在范围
思路分析
先说结论:前序遍历 + 回溯
本题中要获取所有满足路径和为 targetSum 的从根到叶子节点的路径,需要有全局变量记录从根到叶子的路径,当路径之和等于targetSum 时,将其加入到结果列表中。
回溯: 这里有个不算窍门的窍门,问完成一件事情的所有解决方案,一般采用回溯算法(深度优先遍历)完成。
什么时候回溯?
从调用下一层结束后,进行回溯。
程序代码
方法一:使用一个全局变量记录经过的路径
//https://leetcode.cn/problems/path-sum-ii/description/
//113. 路径总和 II
#include<iostream>
#include<vector>
using namespace std;
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 {
private:
vector<vector<int>> res;
vector<int> num;
public:
void travel(TreeNode* root, int targetSum,int curSum)
{
if(root == nullptr)
{
return;
}
num.push_back(root->val);
curSum += root->val;
// 到达叶子节点,然后进行判断
if(root->left == nullptr && root->right == nullptr)
{
if(curSum == targetSum)
{
res.push_back(num);
}
}
travel(root->left,targetSum,curSum);
travel(root->right,targetSum,curSum);
// 回溯,curSum 的回溯可以不写,因为不算全局变量也不是传递引用,这一轮的值改变,不会带回上一轮
// curSum -= root->val;
num.pop_back();
}
vector<vector<int>> pathSum(TreeNode* root, int targetSum) {
travel(root,targetSum,0);
return res;
}
};
上述代码的优化版本,不需要curSum这个变量,直接从targetSum 中减去 root->val ,到叶子节点后判断是否等于0 。
class Solution {
private:
vector<vector<int>> res;
vector<int> num;
public:
void travel(TreeNode* root, int targetSum)
{
if(root == nullptr)
{
return;
}
// 添加路径
num.push_back(root->val);
// 目标值减添加后的值
targetSum -= root->val;
// 到达叶子节点,然后进行判断
if(root->left == nullptr && root->right == nullptr)
{
if(0 == targetSum)
{
res.push_back(num);
}
}
travel(root->left,targetSum);
travel(root->right,targetSum);
// 回溯,恢复这一层的路径和targetSum
// targetSum 这个可以不写,这是C++ 语言特性,因为传参是值复制
targetSum += root->val;
num.pop_back();
}
vector<vector<int>> pathSum(TreeNode* root, int targetSum) {
travel(root,targetSum);
return res;
}
};
隐藏回溯
class Solution {
private:
vector<vector<int>> res;
vector<int> num;
public:
void travel(TreeNode* root, int targetSum)
{
if(root == nullptr)
{
return;
}
num.push_back(root->val);
//targetSum -= root->val;
// 到达叶子节点,然后进行判断
if(root->left == nullptr && root->right == nullptr)
{
if(0 == targetSum - root->val)
{
res.push_back(num);
}
}
// 隐藏回溯到变量中,为什么不需要回溯?因为在当前的这一层中没有改变targetSum 的值
travel(root->left,targetSum - root->val);
travel(root->right,targetSum - root->val);
num.pop_back();
}
vector<vector<int>> pathSum(TreeNode* root, int targetSum) {
travel(root,targetSum);
return res;
}
};
复杂度分析
时间复杂度:O(n),其中 n 是二叉树中的节点个数。每个节点都会遍历一次。
空间复杂度:O(n),其中 n 是二叉树中的节点个数。递归栈的开销,极限情况树是链状的高度为O(N)。
题目感悟
回溯的位置:我们只要在递归之前做出选择,在递归之后撤销刚才的选择
更多相关知识:github.com/pingguo1987…