513.找树左下角的值
给定一个二叉树的 根节点 root
,请找出该二叉树的 最底层 最左边 节点的值。
假设二叉树中至少有一个节点。
提示:
- 二叉树的节点个数的范围是
[1,104]
-231 <= Node.val <= 231 - 1
思路:树左下角的值指的是最后一行最左边的节点。所以需要找到最深的那一层,需要找到最坐标的那一层。在“深度”上需要记录全局的最大深度,在“左右”上需要先遍历左节点后遍历右节点
回溯的过程隐藏在递归调用的下一步!栈的思想
递归三要素:
1 明确递归函数的参数和返回值:传入当前节点和当前深度
2 明确终止条件:如果当前节点是叶子节点的话进一步考虑,如果当前深度 大于 记录的最大深度则说明有可能是最深的层的最左边的节点,记录到result中去。(为什么是大于呢?只有“>”会记录最左边的情况,如果是”>=“在最后一行会从左到右依次替换掉每个节点,直到最右边)
3 确定单层递归的逻辑:递归调用左右节点
/**
* 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:
int maxDepth = INT_MIN;
int result;
// 1 明确递归函数的参数和返回值
void traversal(TreeNode* node, int depth){
// 2 明确终止条件
if(node->left == nullptr && node->right == nullptr){
if(depth > maxDepth){
maxDepth = depth;
result = node->val;
}
}
// 3 确定单层递归的逻辑
if(node->left){
traversal(node->left, depth+1);
}
if(node->right){
traversal(node->right, depth+1);
}
}
int findBottomLeftValue(TreeNode* root) {
traversal(root, 0);
return result;
}
};
在这里省略了回溯,完整呈现回溯的写法如下:
if(node->left){
depth++;
traversal(node->left, depth);
depth--;
}
回溯可以让变量变成递归调用前的样子!类似于栈。
112.路径总和
给你二叉树的根节点 root
和一个表示目标和的整数 targetSum
。判断该树中是否存在 根节点到叶子节点 的路径,这条路径上所有节点值相加等于目标和 targetSum
。如果存在,返回 true
;否则,返回 false
。
叶子节点 是指没有子节点的节点。
提示:
- 树中节点的数目在范围
[0, 5000]
内 -1000 <= Node.val <= 1000
-1000 <= targetSum <= 1000
思路:题目问一棵树中是否存在一条从根节点到叶子节点的一条路径,该路径上的节点值的和等于目标值。如果存在则返回true,否则返回false。遍历整个树,当到叶子节点时路径和满足要求则返回true。问题拆解:当前树(root)中是否存在值为targeNum的路径分成左右子树中是否存在值为targeNum 减 root->val的路径
递归三要素:
1 明确递归函数传入的参数和返回值:传入当前节点和所剩路径,返回 ture or false
2 明确终止条件:碰到叶子节点,如果所剩路径为零,返回true,否则返回false;
3 确定单层递归的逻辑:如果左子树存在,判断左子树递归后的值是否为true;如果有右子树,判断右子树递归后的值是否为true;如果在左右子树递归调用后没返回true,则本函数返回false
/**
* 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:
// 1 明确递归函数的参数和返回值
bool traversal(TreeNode* node, int count){
if(!node){
return false;
}
count -= node->val;
// 2 明确终止条件
if(node->left == nullptr && node -> right == nullptr){
if(count != 0){
return false;
}else{
return true;
}
}
// 3 确定单层递归的逻辑
if(node->left && traversal(node->left, count)) return true;
if(node->right && traversal(node->right, count)) return true;
return false;
}
bool hasPathSum(TreeNode* root, int targetSum) {
return traversal(root, targetSum);
}
};
106.从中序与后序遍历序列构造二叉树
给定两个整数数组 inorder
和 postorder
,其中 inorder
是二叉树的中序遍历, postorder
是同一棵树的后序遍历,请你构造并返回这颗 二叉树 。
提示:
1 <= inorder.length <= 3000
postorder.length == inorder.length
-3000 <= inorder[i], postorder[i] <= 3000
inorder
和postorder
都由 不同 的值组成postorder
中每一个值都在inorder
中inorder
保证是树的中序遍历postorder
保证是树的后序遍历
思路:每一棵树对应的后序遍历数组的最后一个节点一定是根节点。每一棵树对应的中序遍历中根节点左边的是左子树的中序遍历,根节点右边的是右子树的遍历。
通过在后序遍历数组中找到当前根节点,利用根节点在中序遍历数组中找到左右子树的中序遍历数组,利用左右子树的中序遍历数组的个数找到左右子树的后序遍历数组。然后从头构建左右子树
利用递归实现:
1 判断后序遍历数组是否为空,如果为空,直接返回NULL
2 如果不为空,取最后一个元素作为 根节点元素rootval
3 利用遍历中序遍历数组,找到根节点的下标rootindex;
4 根节点之前的数组是左子树的中序遍历数组leftinorder;根节点之后的是右子树的中序遍历数组rightinorder
5 根据左子树的中序遍历数组元素个数可以得到左子树的后序遍历数组leftposorder,进而得出右子树的后序遍历数组rightorder
6 左右子树的中序和后序数组都找到了,递归调用函数就可以得到左右子树了,加入到根节点之下
7 返回根节点
/**
* 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:
TreeNode* traversal(vector<int>& inorder, vector<int>& postorder){
// 1 判断后序遍历数组是否为空,如果为空,直接返回NULL
if(postorder.size() == 0){
return nullptr;
}
// 2 如果不为空,取最后一个元素作为 rootVal
int rootVal = postorder[postorder.size() - 1];
TreeNode* root = new TreeNode(rootVal);
// 3 利用遍历中序遍历数组,找到根节点的下标rootIndex
int rootIndex = 0;
for(rootIndex; rootIndex < inorder.size(); rootIndex++){
if(inorder[rootIndex] == rootVal){
break;
}
}
// 4 根节点之前的数组是左子树的中序遍历数组leftInorder;根节点之后的是右子树的中序遍历数组rightInorder
vector<int> leftInorder(inorder.begin(), inorder.begin() + rootIndex);
vector<int> rightInorder(inorder.begin() + rootIndex + 1, inorder.end());
// 5 根据左子树的中序遍历数组元素个数可以得到左子树的后序遍历数组leftPostorder,进而得出右子树的后序遍历数组rightPostorder
vector<int> leftPostorder(postorder.begin(), postorder.begin() + leftInorder.size());
vector<int> rightPostorder(postorder.begin() + leftInorder.size(), postorder.end() - 1);
// 6 左右子树的中序和后序数组都找到了,递归调用函数就可以得到左右子树了,加入到根节点之下
root->left = traversal(leftInorder, leftPostorder);
root->right = traversal(rightInorder, rightPostorder);
// 7 返回根节点
return root;
}
TreeNode* buildTree(vector<int>& inorder, vector<int>& postorder) {
return traversal(inorder, postorder);
}
};
切片时用的是左闭右开区间!