路径总和 I
给你二叉树的根节点 root 和一个表示目标和的整数 targetSum ,判断该树中是否存在 根节点到叶子节点 的路径,这条路径上所有节点值相加等于目标和 targetSum 。
叶子节点 是指没有子节点的节点。
示例:
输入:root = [5,4,8,11,null,13,4,7,2,null,null,null,1], targetSum = 22
输出:true
思路
使用深度优先遍历的方式来遍历二叉树
-
确定递归函数的参数和返回类型
参数:需要二叉树的根节点,还需要一个计数器,这个计数器用来计算二叉树的一条边之和是否正好是目标和,计数器为int型。
再来看返回值,递归函数什么时候需要返回值,什么时候不需要返回值?
如果需要搜索整棵二叉树,那么递归函数就不要返回值,如果要搜索其中一条符合条件的路径,递归函数就需要返回值,因为遇到符合条件的路径了就要及时返回。
而本题我们要找一条符合条件的路径,所以递归函数需要返回值,及时返回。 -
确定终止条件
计数器如何统计这一条路径的和呢?
用递减,让计数器count初始为目标和,然后每次减去遍历路径节点上的数值。
如果最后count == 0,同时到了叶子节点的话(注意,必须走完,走到叶子节点),说明找到了目标和。
如果遍历到了叶子节点,count不为0,就是没找到。
递归终止条件代码如下:
if (!cur->left && !cur->right && count == 0) return true; // 遇到叶子节点,并且计数为0。
if (!cur->left && !cur->right) return false; // 遇到叶子节点而没有找到合适的边,直接返回。
- 确定单层递归的逻辑
因为终止条件是判断叶子节点,所以递归的过程中就不要让空节点进入递归了。
递归函数是有返回值的,如果递归函数返回true,说明找到了合适的路径,就应该立刻返回。
代码如下:
if (cur->left) {
// 遇到叶子节点返回true,则直接返回true
if (traversal(cur->left, count - cur -> left -> val)) return true; // 注意这里有回溯的逻辑
}
if (cur->right) {
// 遇到叶子节点返回true,则直接返回true
if (traversal(cur->right, count - cur->right->val)) return true; // 回溯
}
return false
以上代码中是包含着回溯的,没有回溯,如何后撤重新找另一条路径呢?
回溯隐藏在traversal(cur->left, count - cur -> left -> val)这里,因为把count - cur -> left -> val直接作为参数传进去,函数结束,count的数值没有改变。
整体代码如下:
class Solution {
private:
bool traversal(TreeNode* cur, int count) {
if (!cur->left && !cur->right && count == 0) return true; // 遇到叶子节点,并且计数为0
if (!cur->left && !cur->right) return false; // 遇到叶子节点直接返回
if (cur->left) { // 左
count -= cur->left->val; // 递归,处理节点;
if (traversal(cur->left, count)) return true;
count += cur->left->val; // 回溯,撤销处理结果
}
if (cur->right) { // 右
count -= cur->right->val; // 递归,处理节点;
if (traversal(cur->right, count)) return true;
count += cur->right->val; // 回溯,撤销处理结果
}
return false;
}
public:
bool hasPathSum(TreeNode* root, int sum) {
if (root == NULL) return false;
return traversal(root, sum - root->val);
}
};
路径总和 II
给你二叉树的根节点 root 和一个整数目标和 targetSum ,找出所有 从根节点到叶子节点 路径总和等于给定目标和的路径。
叶子节点 是指没有子节点的节点。
示例
输入:root = [5,4,8,11,null,13,4,7,2,null,null,5,1], targetSum = 22
输出:[[5,4,11,2],[5,8,4,5]]
思路
要遍历整个树,找到所有路径,递归函数不要返回值!
class Solution {
private:
vector<vector<int>> result;
vector<int> path;
// 递归函数不需要返回值,因为我们要遍历整个树
void traversal(TreeNode* cur, int count) {
if (!cur->left && !cur->right && count == 0) { // 遇到了叶子节点切找到了和为sum的路径
result.push_back(path);
return;
}
if (!cur->left && !cur->right) return ; // 遇到叶子节点而没有找到合适的边,直接返回
if (cur->left) { // 左 (空节点不遍历)
path.push_back(cur->left->val);
count -= cur->left->val;
traversal(cur->left, count); // 递归
count += cur->left->val; // 回溯
path.pop_back(); // 回溯
}
if (cur->right) { // 右 (空节点不遍历)
path.push_back(cur->right->val);
count -= cur->right->val;
traversal(cur->right, count); // 递归
count += cur->right->val; // 回溯
path.pop_back(); // 回溯
}
return ;
}
public:
vector<vector<int>> pathSum(TreeNode* root, int sum) {
result.clear();
path.clear();
if (root == NULL) return result;
path.push_back(root->val); // 把根节点放进路径
traversal(root, sum - root->val);
return result;
}
};
路径总和III
给定一个二叉树,它的每个节点都存放着一个整数值。
找出路径和等于给定数值的路径总数。
路径不需要从根节点还是,也不需要在叶子节点结束,但是路径方向必须是向下的(父节点到子节点)。
root = [10,5,-3,3,2,null,11,3,-2,null,1], sum = 8
10
/
5 -3
/ \
3 2 11
/ \
3 -2 1
返回 3。和等于 8 的路径有:
- 5 -> 3
- 5 -> 2 -> 1
- -3 -> 11
思路:双重DFS
注意,DFS是DFS,回溯是回溯。回溯要有撤销处理的操作,但是DFS可以没有!
- 先序递归遍历每个节点
- 以每个节点作为起始节点DFS寻找满足条件的路径
class Solution {
public:
int ans = 0;
void dfs(TreeNode* root, int sum)
{
if (root == nullptr)
return;
sum -= root->val;
if (sum == 0)
ans++;
dfs(root -> left, sum);
dfs(root -> right, sum);
}
int pathSum(TreeNode* root, int sum)
{
if (root == nullptr)
return ans;
dfs(root, sum);
pathSum(root->left, sum);
pathSum(root->right, sum);
return ans;
}
};
复杂度分析
- 时间复杂度:O(n)
- 空间复杂度:O(n)