代码随想录算法训练营第16天 | 513.找树左下角的值、112.路径总和、113.路径总和Ⅱ、106.使用中序与后序遍历序列构造二叉树、105.使用前序遍历和中序遍历序列构造二叉树
文章目录
513.找树左下角的值
解题思路
本题可以使用递归,也可以使用迭代,思路分别如下:
递归法
- 确定递归函数的参数和返回值:参数需要遍历的树的根节点,还有一个 int 变量来记录最长深度,无需返回值
- 终止条件:遇到叶子节点
- 单层递归的逻辑:在找最大深度时,需要用到回溯
迭代法
使用层序遍历,记录最后一行第一个节点的数值。
代码实现
递归法
class Solution {
public:
int maxDepth = INT_MIN;
int result;
void traversal(TreeNode* root, int depth) {
if (root->left == nullptr && root->right == nullptr) {
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;
}
};
迭代法
class Solution {
public:
int findBottomLeftValue(TreeNode* root) {
queue<TreeNode*> que;
if (root != nullptr)
que.push(root);
int result = 0;
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;
}
};
题目总结
本题的两种解法总结了之前所学的知识,递归里面包含回溯的过程以及层序遍历。
112.路径总和
解题思路
递归法
- 递归函数的参数和返回值:参数首先是二叉树的根节点和计数器,计数器用来计算二叉树一条边的节点之和是否为目标值。
- 终止条件:判断节点数值是否等于目标值的代码会很麻烦,可以将计数器设置为目标值,每次减去遍历路径节点上的数值,若最后等于0,同时遍历到了叶子节点,则说明找到了目标和。
代码实现
class Solution {
public:
bool traversal(TreeNode* cur, int count) {
if (!cur->left && !cur->right && count == 0)
return true;
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;
}
bool hasPathSum(TreeNode* root, int targetSum) {
if (root == nullptr)
return false;
return traversal(root, targetSum - root->val);
}
};
题目总结
终止条件时将计数器设置为目标值,每次减去遍历路径节点上的数值能够简化代码
113.路径总和Ⅱ
解题思路
本题和上一题的区别在于需要找出所有符合条件的路径,这就需要遍历整个二叉树,多了一个记录符合条件的路径的操作。
代码实现
class Solution {
public:
vector<vector<int>> result;
vector<int> path;
void traversal(TreeNode* cur, int count) {
if (!cur->left && !cur->right && count == 0) {
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;
}
vector<vector<int>> pathSum(TreeNode* root, int targetSum) {
result.clear();
path.clear();
if (root == nullptr)
return result;
path.push_back(root->val);
traversal(root, targetSum - root->val);
return result;
}
};
题目总结
多一个记录路径的操作,并且将所有符合条件的路径都放到一个容器中
106.使用中序与后序遍历序列构造二叉树
解题思路
以后序数组的最后一个元素作为切割点,先切割中序数组,然后根据中序数组,反过来切割后序数组,一层一层切下去,每次后序数组的最后一个元素就是节点元素。
递归法步骤:
- 第一步:如果数组大小为零的话,说明是空节点。
- 第二步:如果不为空,那么取后序数组最后一个元素作为节点元素。
- 第三步:找到后序数组最后一个元素在中序数组的位置,作为切割点
- 第四步:切割中序数组,切成中序左数组和中序右数组 (顺序别搞反了,一定是先切中序数组)
- 第五步:切割后序数组,切成后序左数组和后序右数组
- 第六步:递归处理左区间和右区间
代码实现
class Solution {
public:
TreeNode* traversal(vector<int>& inorder, int inorderBegin, int inorderEnd,
vector<int>& postorder, int postorderBegin,
int postorderEnd) {
if (postorderBegin == postorderEnd)
return nullptr;
int rootValue = postorder[postorderEnd - 1];
TreeNode* root = new TreeNode(rootValue);
if (postorderEnd - postorderBegin == 1)
return root;
int delimiterIndex;
for (delimiterIndex = inorderBegin; delimiterIndex < inorderEnd;
delimiterIndex++) {
if (inorder[delimiterIndex] == rootValue)
break;
}
int leftInorderBegin = inorderBegin;
int leftInorderEnd = delimiterIndex;
int rightInorderBegin = delimiterIndex + 1;
int rightInorderEnd = inorderEnd;
int leftPosterBegin = postorderBegin;
int leftPosterEnd = postorderBegin + delimiterIndex - inorderBegin;
int rightPosterBegin = postorderBegin + delimiterIndex - inorderBegin;
int rightPosterEnd = postorderEnd - 1;
root->left = traversal(inorder, leftInorderBegin, leftInorderEnd,
postorder, leftPosterBegin, leftPosterEnd);
root->right = traversal(inorder, rightInorderBegin, rightInorderEnd,
postorder, rightPosterBegin, rightPosterEnd);
return root;
}
TreeNode* buildTree(vector<int>& inorder, vector<int>& postorder) {
if (inorder.size() == 0 || postorder.size() == 0)
return nullptr;
return traversal(inorder, 0, inorder.size(), postorder, 0,
postorder.size());
}
};
题目总结
注意在切割过程中,坚持循环不变量原则,明确切割的标准,左闭右开还是左开右闭。
105.使用前序遍历和中序遍历序列构造二叉树
解题思路
和上题原理相同,通过前序遍历的第一个元素作为切割节点,先切割中序数组,再返回来切割前序数组。
代码实现
class Solution {
public:
TreeNode* traversal(vector<int>& inorder, int inorderBegin, int inorderEnd,
vector<int>& preorder, int preorderBegin,
int preorderEnd) {
if (preorderBegin == preorderEnd)
return nullptr;
int rootValue = preorder[preorderBegin];
TreeNode* root = new TreeNode(rootValue);
if (preorderEnd - preorderBegin == 1)
return root;
int delimiterIndex;
for (delimiterIndex = inorderBegin; delimiterIndex < inorderEnd;
delimiterIndex++) {
if (inorder[delimiterIndex] == rootValue)
break;
}
int leftInorderBegin = inorderBegin;
int leftInorderEnd = delimiterIndex;
int rightInorderBegin = delimiterIndex + 1;
int rightInorderEnd = inorderEnd;
int leftPreorderBegin = preorderBegin + 1;
int leftPreorderEnd = preorderBegin + 1 + delimiterIndex - inorderBegin;
int rightPreorderBegin =
preorderBegin + 1 + delimiterIndex - inorderBegin;
int rightPreorderEnd = preorderEnd;
root->left = traversal(inorder, leftInorderBegin, leftInorderEnd,
preorder, leftPreorderBegin, leftPreorderEnd);
root->right = traversal(inorder, rightInorderBegin, rightInorderEnd,
preorder, rightPreorderBegin, rightPreorderEnd);
return root;
}
TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder) {
if (inorder.size() == 0 || preorder.size() == 0) {
return nullptr;
}
return traversal(inorder, 0, inorder.size(), preorder, 0,
preorder.size());
}
};