刷题的第十五天,希望自己能够不断坚持下去,迎来蜕变。😀😀😀
刷题语言:C++
Day15 任务
● 513.找树左下角的值
● 112. 路径总和 113.路径总和ii
● 106.从中序与后序遍历序列构造二叉树 105.从前序与中序遍历序列构造二叉树
本题要找出树的最后一行最左边的值
思路1:层序遍历
思路2:递归
迭代法
层序遍历模板参考代码随想录刷题题Day12
class Solution {
public:
int findBottomLeftValue(TreeNode* root) {
queue<TreeNode*> que;
int result;
if (root != NULL) que.push(root);
while (!que.empty())
{
int size = que.size();
for (int i = 0; i < size; i++)
{
TreeNode* node = que.front();
que.pop();
if (i == 0) result = node->val;// 记录最后一行第一个元素
if (node->left) que.push(node->left);
if (node->right) que.push(node->right);
}
}
return result;
}
};
递归法
误区:不是一直向左遍历,最后一个就是答案
一直向左遍历到最后一个,未必是最后一行
关键:在树的最后一行找到最左边的值
(1) 判断最后一行:深度最大的叶子节点
(2) 最左边的值:可以使用前序遍历(当然中序,后序都可以,因为本题没有 中间节点的处理逻辑,只要左优先就行),保证优先左边搜索,然后记录深度最大的叶子节点,此时就是树的最后一行最左边的值。
(1)确定递归函数的参数和返回值
参数:要遍历的树的根节点,最长深度
返回值:void
int maxDepth = INT_MIN;// 全局变量 记录最大深度
int result; // 全局变量 最大深度最左节点的数值
void traversal(TreeNode* node, int depth)
(2)确定终止条件
当遇到叶子节点的时候,就需要统计一下最大的深度了,所以需要遇到叶子节点来更新最大深度。
if (node->left == NULL && node->right == NULL)
{
if (depth > maxDepth)
{
maxDepth = depth; // 更新最大深度
result = node->val; // 最大深度最左面的数值
}
return;
}
(3)确定单层递归的逻辑
找最大深度的时候,递归的过程中依然要使用回溯
// 中
if (node->left) {// 左
depth++;// 深度加一
traversal(node->left, depth);
depth--;// 回溯,深度减一
}
if (node->right) {// 右
depth++;// 深度加一
traversal(node->right, depth);
depth--;// 回溯,深度减一
}
C++:
class Solution {
public:
int maxDepth = INT_MIN;
int result;
void traversal(TreeNode* node, int depth) {
if (node->left == NULL && node->right == NULL) {
if (maxDepth < depth) {
maxDepth = depth;
result = node->val;
}
}
if (node->left) {
depth++;
traversal(node->left, depth);
depth--;
}
if (node->right) {
depth++;
traversal(node->right, depth);
depth--;
}
return;
}
int findBottomLeftValue(TreeNode* root) {
traversal(root, 0);
return result;
}
};
精简版本C++:
class Solution {
public:
int maxDepth = INT_MIN;
int result;
void traversal(TreeNode* node, int depth) {
if (node->left == NULL && node->right == NULL) {
if (maxDepth < depth) {
maxDepth = depth;
result = node->val;
}
}
if (node->left) {
traversal(node->left, depth + 1);// 隐藏着回溯
}
if (node->right) {
traversal(node->right, depth + 1);// 隐藏着回溯
}
return;
}
int findBottomLeftValue(TreeNode* root) {
traversal(root, 0);
return result;
}
};
思路:
使用深度优先遍历的方式,本题前中后序都可以,因为中间节点没有处理逻辑
递归法
(1)确定递归函数的参数和返回类型
参数:二叉树的根节点、计算器(用来计算二叉树的一条边之和是否正好是目标和)
返回值:要找一条符合条件的路径,所以递归函数需要返回值,遍历的路线,并不要遍历整棵树,及时返回,返回类型是bool
递归函数返回值:
(1)如果需要搜索整棵二叉树且不用处理递归返回值,递归函数就不要返回值
(2)如果需要搜索整棵二叉树且需要处理递归返回值,递归函数就需要返回值。
(3)如果要搜索其中一条符合条件的路径,那么递归一定需要返回值,因为遇到符合条件的路径了就要及时返回
bool traversal(TreeNode* node, int count)
(2)确定终止条件
计数器count初始为目标和,然后每次减去遍历路径节点上的数值
if (node->left == NULL && node->right == NULL && count == 0) return true;
if (node->left == NULL && node->right == NULL && count != 0) return false;
(3)确定单层递归的逻辑
递归函数是有返回值的,如果递归函数返回true,说明找到了合适的路径,应该立刻返回
if (node->left) {// 左 (空节点不遍历)
// 遇到叶子节点返回true,则直接返回true
if (traversal(node->left, count - node->left->val)) return true;
}
if (node->right) {// 右 (空节点不遍历)
// 遇到叶子节点返回true,则直接返回true
if (traversal(node->right, count - node->right->val)) return true;
return false;
把回溯的过程表现出来:
if (node->left) {// 左
count -= node->left->val;// 递归,处理节点;
if (traversal(node->left, count)) return true;
count += node->left->val;// 回溯,撤销处理结果
}
if (node->right) { // 右
count -= node->right->val;
if (traversal(node->right, count)) return true;
count += node->right->val;// 回溯,撤销处理结果
}
C++:
class Solution {
public:
bool traversal(TreeNode* node, int count) {
if (node->left == NULL && node->right == NULL && count == 0) return true;// 遇到叶子节点,并且计数为0
if (node->left == NULL && node->right == NULL && count != 0) return false;// 遇到叶子节点直接返回
if (node->left) {// 左
count -= node->left->val;// 递归,处理节点;
if (traversal(node->left, count)) return true;
count += node->left->val;// 回溯,撤销处理结果
}
if (node->right) {// 右
count -= node->right->val;// 递归,处理节点
if (traversal(node->right, count)) return true;
count += node->right->val;// 回溯,撤销处理结果
}
return false;
}
bool hasPathSum(TreeNode* root, int targetSum) {
if (root == NULL) return false;
return traversal(root, targetSum - root->val);
}
};
精简版本C++:
class Solution {
public:
bool hasPathSum(TreeNode* root, int targetSum) {
if (!root) return false;
if (!root->left && !root->right && targetSum == root->val) {
return true;
}
return hasPathSum(root->left, targetSum - root->val) || hasPathSum(root->right, targetSum - root->val);
}
};
思路:
路径总和ii要遍历整个树,找到所有路径,所以递归函数不要返回值!
class Solution {
public:
vector<vector<int>> result;
vector<int> path;
// 递归函数不需要返回值,因为我们要遍历整个树
void traversal(TreeNode* node, int count) {
if (node->left == NULL && node->right == NULL && count == 0) {
result.push_back(path);
return;
}
if (node->left == NULL && node->right == NULL) return;// 遇到叶子节点而没有找到合适的边,直接返回
if (node->left) {// 左 (空节点不遍历)
path.push_back(node->left->val);
count -= node->left->val;
traversal(node->left, count);// 递归
count += node->left->val;// 回溯
path.pop_back();// 回溯
}
if (node->right) {// 右 (空节点不遍历)
path.push_back(node->right->val);
count -= node->right->val;
traversal(node->right, count);// 递归
count += node->right->val;// 回溯
path.pop_back();// 回溯
}
return;
}
vector<vector<int>> pathSum(TreeNode* root, int targetSum) {
result.clear();
path.clear();
if (root == NULL) return result;
path.push_back(root->val);// 把根节点放进路径
traversal(root, targetSum - root->val);
return result;
}
};
思路:
- 后序数组为0,空节点
- 后序数组最后一个元素为节点元素
- 寻找中序数组位置作为切割点
- 切中序数组
- 切后序数组
- 递归处理左右区间
C++:
class Solution {
public:
TreeNode* traversal(vector<int>& inorder, vector<int>& postorder) {
if (postorder.size() == 0) return NULL;
// 后序遍历数组最后一个元素,就是当前的中间节点
int rootValue = postorder[postorder.size() - 1];
TreeNode* root = new TreeNode(rootValue);
// 叶子节点
if (postorder.size() == 1) return root;
// 找到中序遍历的切割点
int index;
for (index = 0; index < inorder.size(); index++)
{
if (inorder[index] == rootValue) break;
}
// 切割中序数组
vector<int> leftInorder(inorder.begin(), inorder.begin() + index);
vector<int> rightInorder(inorder.begin() + index + 1, inorder.end());
postorder.resize(postorder.size() - 1);
// 切割后序数组
vector<int> leftPostorder(postorder.begin(), postorder.begin() + leftInorder.size());
vector<int> rightPostorder(postorder.begin() + leftInorder.size(), postorder.end());
root->left = traversal(leftInorder, leftPostorder);
root->right = traversal(rightInorder, rightPostorder);
return root;
}
TreeNode* buildTree(vector<int>& inorder, vector<int>& postorder) {
if (inorder.size() == 0 || postorder.size() == 0) return NULL;
return traversal(inorder, postorder);
}
};
思路:
- 前序数组为0,空节点
- 前序数组第一个元素为节点元素
- 寻找中序数组位置作为切割点
- 切中序数组
- 切前序数组
- 递归处理左右区间
C++:
class Solution {
public:
TreeNode* traversal(vector<int>& preorder, vector<int>& inorder) {
// 前序数组为0,空节点
if (preorder.size() == 0) return NULL;
// 前序数组第一个元素为节点元素
int rootValue = preorder[0];
TreeNode* root = new TreeNode(rootValue);
if (preorder.size() == 1) return root;
// 寻找中序数组位置作为切割点
int index;
for (index = 0; index < inorder.size(); index++) {
if (inorder[index] == rootValue) break;
}
// 切中序数组
vector<int> leftInorder(inorder.begin(), inorder.begin() + index);
vector<int> rightInorder(inorder.begin() + index + 1, inorder.end());
// 切前序数组
preorder.erase(preorder.begin());
vector<int> leftPreorder(preorder.begin(), preorder.begin() + leftInorder.size());
vector<int> rightPreorder(preorder.begin() + leftPreorder.size(), preorder.end());
// 递归处理左右区间
root->left = traversal(leftPreorder, leftInorder);
root->right = traversal(rightPreorder, rightInorder);
return root;
}
TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder) {
if (preorder.size() == 0 || inorder.size() == 0) return NULL;
return traversal(preorder, inorder);
}
};
鼓励坚持十六天的自己😀😀😀