404.左叶子之和
思路
本题中最难的就是,判断当前节点是不是左叶子是无法判断的,必须要通过节点的父节点来判断其左孩子是不是左叶子
如果该节点的左节点不为空,该节点的左节点的左节点为空,该节点的左节点的右节点为空,则找到了一个左叶子。
关于遍历顺序:
这个题目用后序遍历是最方便的,因为我们要一层一层向上返回,如果对于一个根结点,我们收集了左子树的左叶子之和、右子树的左叶子之和,然后返回给上一层,那么我们的父结点需要把左子树的结果+右子树的结果,这样才计算出根结点的左叶子之和
伪代码
- 确定参数和返回值
int traversal(TreeNode node){
}
- 确定终止条件
if (node == NULL) return 0;
//为什么这里return 0是因为遍历到叶子结点了,当叶子结点作为根结点的时候,左右孩子都是空,当然要返回零
//如果我们想收集叶子结点的值,我们必须只遍历到叶子结点的父结点就打止
if (node->left == NULL && node->right == NULL) return 0;
- 单层递归逻辑
//遍历左
int leftNum = traversal(node->left);
if (node->left != NULL && node->left->left == NULL && node->left->right == NULL)
leftNum = node->left->val;
//遍历右
int rightNum = traversal(node->right);
//中
int sum = leftNum + rightNum;
return sum
513.找树左下角的值
文章讲解:513.找树左下角的值
本题要找出树的最后一行的最左边的值。此时大家应该想起用层序遍历是非常简单的了,反而用递归的话会比较难一点。
如何找到左下角
首先题目要求:在树的最后一行找到最左边的值。
所以我们有两个任务:
- 最后一行:深度最大的叶子节点一定是最后一行
- 最左边的值:可以使用前序遍历(当然中序,后序都可以,因为本题没有 中间节点的处理逻辑,只要左优先就行),保证优先左边搜索,然后记录深度最大的叶子节点,此时就是树的最后一行最左边的值。
伪代码
- 确定递归函数的参数和返回值
参数必须有要遍历的树的根节点,还有就是一个int型的变量用来记录最长深度。 这里就不需要返回值了,所以递归函数的返回类型为void。
int maxDepth = INT_MIN; // 全局变量 记录最大深度
int result; // 全局变量 最大深度最左节点的数值
void traversal(TreeNode* root, int depth)
- 确定终止条件
当遇到叶子节点的时候,就需要统计一下最大的深度了,所以需要遇到叶子节点来更新最大深度。
if (root->left == NULL && root->right == NULL) {
if (depth > maxDepth) {
maxDepth = depth; // 更新最大深度
result = root->val; // 最大深度最左面的数值
}
return;
}
-
确定单层递归逻辑
在找最大深度的时候,递归的过程中依然要使用回溯
// 中
if (root->left) { // 左
depth++; // 深度加一
traversal(root->left, depth);
depth--; // 回溯,深度减一
}
if (root->right) { // 右
depth++; // 深度加一
traversal(root->right, depth);
depth--; // 回溯,深度减一
}
return;
CPP代码实现
class Solution {
public:
int maxDepth = INT_MIN;
int result;
void traversal(TreeNode* root, int depth) {
if (root->left == NULL && root->right == NULL) {
if (depth > maxDepth) {
maxDepth = depth;
result = root->val;
}
return;
}
if (root->left) {
depth++;
traversal(root->left, depth);
depth--; // 回溯
}
if (root->right) {
depth++;
traversal(root->right, depth);
depth--; // 回溯
}
return;
}
int findBottomLeftValue(TreeNode* root) {
traversal(root, 0);
return result;
}
};
112.路经总和
文章讲解:112.路经总和
大体思路
因为本题并不涉及中结点的处理逻辑,所以前中后序的遍历顺序都是可以的。
其实基本规则就是在遍历的过程中一直把结点值加起来,等遍历到叶子结点看是不是等于目标值即可。
但其实具体的代码实现还是比较难的。
伪代码
- 确定返回值和参数:由于在本题中,我们只要有一条路径满足要求即可返回,是没有必要遍历所有结点的。所以我们就要依靠这个返回值bool。这里我们的参数count直接传目标值,我们每遍历到一个结点,就对count做一个减法。如果到叶子结点count被减成0了,那就分会true
bool traversal(TreeNode* node, int count)
- 确定终止条件
遇到叶子结点我们就要判断该路径是不是我们想要的路径
if(node->left == NULL && node->right == NULL && count == 0) return true;
if(node->left == NULL && node->right == NULL && count != 0) return false;
- 单层递归逻辑
首先写左逻辑,如果向左递归,我们的count就把对应的值减下去,并且向左递归
//向左遍历逻辑
if (node->left){
count -= node->left->val;
if(traversal(node->left, count)) return true; //注意这里的traversal()函数有返回值,如果这里已经返回ture了,那么说明路径已经被找到了
count += node->left->val;//为嘛有这个回溯:我们回退到上层结点的时候,一定要把它加上来
}
//向右遍历
if (node->right){
count -= node->right->val;
if (traversal(node->right, count)) return true;
count += node->right->val;
}
return false; //如果都没有返回true那就返回false喽
上述的遍历逻辑可以写成
if (cur->left) { // 左 (空节点不遍历)
// 遇到叶子节点返回true,则直接返回true
if (traversal(cur->left, count - cur->left->val)) return true; // 注意这里有回溯的逻辑
//代码段后面的count该是什么值还是什么值
}
if (cur->right) { // 右 (空节点不遍历)
// 遇到叶子节点返回true,则直接返回true
if (traversal(cur->right, count - cur->right->val)) return true; // 注意这里有回溯的逻辑
}
return false;
CPP代码
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);
}
};
//精简后的代码
class Solution {
public:
bool hasPathSum(TreeNode* root, int sum) {
if (!root) return false;
if (!root->left && !root->right && sum == root->val) {
return true;
}
return hasPathSum(root->left, sum - root->val) || hasPathSum(root->right, sum - root->val);
}
};