代码随想录算法训练营第十八天|LeetCode513. 找树左下角的值、LeetCode112.路径总和、LeetCode113.路径总和 II、LeetCode106.从中序与后序遍历序列构造二叉树、LeetCode105.从前序与中序遍历序列构造二叉树
2023年3月18日
第三十二天补
文章目录
513. 找树左下角的值
题目链接:513. 找树左下角的值
思路:
- 这道题的意思其实是找最底层的最左侧元素的值,所以用层序遍历的思路是最容易理解的
- 同时这道题我还用一个递归的思路来解答,所以一共会写两种
时间复杂度:O(n)
,空间复杂度O(n)
迭代法
- 由于常规层序遍历迭代的思路,是每层从左至右,等到当前层的size减到0再记录下一层
- 这样的顺序无法判断是否是当前层的第一个元素
- 所以我们在便利的顺序选择从又向左遍历,遍历到最左侧的时候更新
代码:
class Solution
{
public:
int findBottomLeftValue(TreeNode *root)
{
std::queue<TreeNode *> nodeQue;
int cur_size = 0;
int next_size = 0;
if (root != nullptr)
{
nodeQue.push(root);
++cur_size;
}
while (!nodeQue.empty())
{
auto node = nodeQue.front();
// 先插入右孩子,再插入左孩子
if (node->right != nullptr)
{
nodeQue.push(node->right);
++next_size;
}
if (node->left != nullptr)
{
nodeQue.push(node->left);
++next_size;
}
--cur_size;
nodeQue.pop();
// 遍历到最左侧元素,更新result
if (cur_size == 0)
{
cur_size = next_size;
next_size = 0;
result = node->val;
}
}
return result;
}
int result = 0;
};
递归法
- 找最底层节点最左侧节点的值,如果用递归便利的话,我们会从左到右依次遍历到所有的叶子节点
- 不论是前序遍历,中序遍历,还是后序遍历都是同样的
- 所以在这道题我们可以每遍历到一个叶子节点的时候记录一个最大深度
- 如果当前节点的深度比最大深度更深,就更新最左节点的值
- 又由于从左向右遍历叶子的顺序,对于同层节点,只会更新最左侧的值
代码:
class Solution
{
public:
int findBottomLeftValue(TreeNode *root)
{
int result;
// 记录当前深度
int depth = 1;
travelNode(depth+1,result,root);
return result;
}
void travelNode(int depth, int &result, TreeNode *node)
{
// 只有是叶子节点才有可能更新
if (node->left == nullptr && node->right == nullptr)
{
// 深度大于最大深度则更新
if (depth > maxDepth) {
result = node->val;
maxDepth = depth;
}
}
// 不是叶子节点就递归
else
{
// 注意要告诉下一轮递归它的深度
if (node->left != nullptr)travelNode(depth+1, result, node->left);
if (node->right != nullptr)travelNode(depth+1, result, node->right);
}
}
int maxDepth = INT32_MIN;
};
总结:
- 迭代比较容易写出来想到,递归需要看视频才能理解
- 做题体验:
哈哈
(哈哈
>嘿嘿
>哎哎
>嘤嘤
) - 时间:
未计
一刷
112. 路径总和
题目链接:112. 路径总和
思路:
- 这道题要累加一个和,当到叶子节点的时候判断和是否是目标值即可
时间复杂度:O(n)
,空间复杂度O(n)
代码:
class Solution {
public:
bool hasPathSum(TreeNode* root, int targetSum) {
int sum = 0;
if(root)
{
return travelNode(root,sum,targetSum);
}
else
{
return false;
}
}
bool travelNode(TreeNode * node ,int sum,int targetSum)
{
// 默认值都是“假”
// 对于空孩子,得到的返回值是“假”,不然有空孩子的节点都变成了“真 ”
bool left = false;
bool right = false;
sum += node->val;
// 到了叶子节点的时候判断
if(node->left == nullptr && node->right == nullptr)
{
if(sum == targetSum)return true;
else return false;
}
// 没到叶子节点的时候递归
else
{
if(node->left)left = travelNode(node->left,sum,targetSum);
if(node->right)right= travelNode(node->right,sum,targetSum);
// 左右孩子中只要找到任意一条路径,就返回“真”
return left || right;
}
}
};
总结:
- 这道题理解起来不难,但感觉在代码实现中,还是要理清思路
- 做题体验:
哈哈
(哈哈
>嘿嘿
>哎哎
>嘤嘤
) - 时间:
未计
一刷
113. 路径总和 II
题目链接:113. 路径总和 II
思路:
- 这道题比112多了返回路径,那我们在叶子节点判断为真的时候要执行的操作就是向结果集插入路径
- 同时要在递归的过程中生成路径的信息
时间复杂度:O(n)
,空间复杂度O(n)
代码:
class Solution {
public:
vector<vector<int>> pathSum(TreeNode* root, int targetSum) {
vector<int> path;
int sum = 0;
if(root)
{
travelNode(root,path,sum,targetSum);
}
return result;
}
void travelNode(TreeNode*node,vector<int>&path,int sum,int targetSum)
{
sum += node->val;
path.push_back(node->val);
// 是叶子节点就判断
if(node->left == nullptr && node->right == nullptr)
{
if(sum == targetSum)result.push_back(path);
}
// 不是叶子节点就递归,记得要有回溯的抛出路径的操作
else
{
if(node->left !=nullptr)
{
travelNode(node->left,path,sum,targetSum);
path.pop_back();
}
if(node->right !=nullptr)
{
travelNode(node->right,path,sum,targetSum);
path.pop_back();
}
}
}
vector<vector<int>> result;
};
总结:
- 257和112的结合,提升了综合能力
- 做题体验:
哈哈
(哈哈
>嘿嘿
>哎哎
>嘤嘤
) - 时间:
未计
一刷
106. 从中序与后序遍历序列构造二叉树
题目链接:106. 从中序与后序遍历序列构造二叉树
思路:
- 这道题的关键,就是从逻辑上理解,如何用一个中序遍历和一个后序遍历确定一个二叉树
- 对于一个后序遍历,它的最后一个节点就是这个树的根,而找到根,就可以在中序遍历里知道左右子树的节点个数
- 进而可以到后序遍历的数组里面找到代表左右子树的后序遍历,又能找到左右子树的根
- 这样就可以形成递归
- 而这道题最难得地方,也许就是确定递归时的数组参数的范围
时间复杂度:O(n)
,空间复杂度O(n)
代码:
class Solution
{
public:
TreeNode *buildTree(vector<int> &inorder, vector<int> &postorder)
{
// pos代表的是根节点在中序遍历数组中的位置
// 还代表了左子树的节点个数
int pos = 0;
int val = postorder[postorder.size()-1];
for (int i = 0; i < inorder.size(); ++i)
{
if (inorder[i] == val)
{
pos = i;
}
}
TreeNode *root = new TreeNode(val);
if (pos != 0)
{
// 左子树的中序遍历,是从数组头开始的,长度为pos
// 左子树的后序遍历,也是从数组头开始的,长度为pos
std::vector<int> leftInorder(inorder.begin(), inorder.begin() + pos);
std::vector<int> leftPostorder(postorder.begin(), postorder.begin() + pos );
root->left = buildTree(leftInorder, leftPostorder);
}
if (pos != postorder.size() - 1)
{
// 右子树的中序遍历,是从第pos+1位开始的,到数组末尾结束
// 右子树的后序遍历,是从第pos位开始的,到数组末尾前一位结束
std::vector<int> rightInorder(inorder.begin() + pos + 1, inorder.end());
std::vector<int> rightPostorder(postorder.begin() + pos, postorder.begin() + postorder.size() -1);
root->right = buildTree(rightInorder, rightPostorder);
}
return root;
}
};
总结:
- 这道题做的时候要慢点,理清楚哦
- 做题体验:
哈哈
(哈哈
>嘿嘿
>哎哎
>嘤嘤
) - 时间:
未计
一刷
105. 从前序与中序遍历序列构造二叉树
题目链接:105. 从前序与中序遍历序列构造二叉树
思路:
- 这道题和106本质上是一道题,这里不赘述了
时间复杂度:O(n)
,空间复杂度O(n)
代码:
class Solution
{
public:
TreeNode *buildTree(vector<int> &preorder, vector<int> &inorder)
{
int pos = 0;
int val = preorder[0];
for (int i = 0; i < inorder.size(); ++i)
{
if (inorder[i] == val)
{
pos = i;
}
}
TreeNode *root = new TreeNode(val);
if (pos != 0)
{
std::vector<int> m_Inorder(inorder.begin(), inorder.begin() + pos);
std::vector<int> m_Preorder(preorder.begin() + 1, preorder.begin() + pos + 1);
root->left = buildTree(m_Preorder,m_Inorder);
}
if (pos != preorder.size() - 1)
{
std::vector<int> m_Inorder(inorder.begin() + pos + 1, inorder.end());
std::vector<int> m_Preorder(preorder.begin() + pos + 1, preorder.begin() + preorder.size());
root->right = buildTree(m_Preorder,m_Inorder);
}
return root;
}
};
总结:
- 加油哦
- 做题体验:
哈哈
(哈哈
>嘿嘿
>哎哎
>嘤嘤
) - 时间:
未计
一刷