目录:
题目链接:
https://leetcode.cn/problems/find-bottom-left-tree-value/
给定一个二叉树的 根节点 root
,请找出该二叉树的 最底层 最左边 节点的值。
假设二叉树中至少有一个节点。
示例 1:
!https://assets.leetcode.com/uploads/2020/12/14/tree1.jpg
输入:root = [2,1,3]
输出:1
思考:递归和迭代都可以。
递归:题目关键点,最底层,所以,还需要知道子树的深度。可以考虑返回一个数组信息,第一个传的是该分支最左边节点的值,第二个传深度信息,也就是该分支的高度。
根据随想录写的代码:
class Solution {
public:
int maxDepth = INT_MIN;
int result;
void reversal(TreeNode* root, int depth) {
if (root->left == NULL && root ->right == NULL) {
if (depth > maxDepth) {
maxDepth = depth;
result = root->val;
}
}
if (root->left) {
depth++;
reversal(root->left, depth);
depth--;// 回溯
}
if (root->right) {
depth++;
reversal(root->right,depth);
depth--; // 回溯
}
return;
}
int findBottomLeftValue(TreeNode* root) {
reversal(root, 0);
return result;
}
};
迭代法:
一直用一个值保存每行的第一个,遍历结束后就放的是最底层的第一个。
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;
}
};
递归的处理方法很妙,我原先想的是返回一个数组,分别记录高度和最左节点的值。随想录里面是在递归外层用变量记录最大的深度和对于的值。这种方法比我刚开始思考的方法传回的值更简洁。后面如果遇到这种需要记录的题目,也可以考虑用这种回溯的思路去做,在递归的外层用变量记录。
112. 路径总和
给你二叉树的根节点 root
和一个表示目标和的整数 targetSum
。判断该树中是否存在 根节点到叶子节点 的路径,这条路径上所有节点值相加等于目标和 targetSum
。如果存在,返回 true
;否则,返回 false
。
叶子节点 是指没有子节点的节点。
示例 1:
!https://assets.leetcode.com/uploads/2021/01/18/pathsum1.jpg
输入:root = [5,4,8,11,null,13,4,7,2,null,null,null,1], targetSum = 22
输出:true
解释:等于目标和的根节点到叶节点路径如上图所示。
思考:这题可以用前序递归来试一下,用一个数组记录一下从根节点到当前叶子节点的路径和。
代码:
这里就很明显用到了回溯的思想,比较坑的是结束情况需要考虑的更清楚。起先没想到root为空,判断条件少了一个,卡了我将近二十分钟。
class Solution {
public:
bool hasPathSum(TreeNode* root, int targetSum) {
if (root == NULL) return false;
if (root->left == NULL && root->right == NULL && root->val == targetSum) return true;
if (!root->left && !root->right && root->val != targetSum) return false;
if (root->left) {
targetSum -= root->val;
if(hasPathSum(root->left,targetSum)) return true;
targetSum += root->val;
}
if (root->right) {
targetSum -= root->val;
if(hasPathSum(root->right, targetSum)) return true;
targetSum += root->val;
}
return false;
}
};
迭代也能行,试着写一下。没写出来,直接看随想录代码:
递归代码:
思路差不多一样的
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);
}
};
//精简版代码
lass 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);
}
};
迭代法
利用一个stack栈,用pair的形式存储当前节点的指针以及路径的累计数值。很秒。
class solution {
public:
bool haspathsum(TreeNode* root, int sum) {
if (root == null) return false;
// 此时栈里要放的是pair<节点指针,路径数值>
stack<pair<TreeNode*, int>> st;
st.push(pair<TreeNode*, int>(root, root->val));
while (!st.empty()) {
pair<TreeNode*, int> node = st.top();
st.pop();
// 如果该节点是叶子节点了,同时该节点的路径数值等于sum,那么就返回true
if (!node.first->left && !node.first->right && sum == node.second) return true;
// 右节点,压进去一个节点的时候,将该节点的路径数值也记录下来
if (node.first->right) {
st.push(pair<TreeNode*, int>(node.first->right, node.second + node.first->right->val));
}
// 左节点,压进去一个节点的时候,将该节点的路径数值也记录下来
if (node.first->left) {
st.push(pair<TreeNode*, int>(node.first->left, node.second + node.first->left->val));
}
}
return false;
}
};
113. 路径总和 II
这道题升级了一下难度,返回的不再是bool,而是所有等于目标和的路径。
递归方法:
自己写的方法,时间复杂度有点高,如果将vec换成指针,或许会快不少。
class Solution {
public:
vector<vector<int>> result;
vector<int> vec;
void findPath(TreeNode* root, int targetSum, vector<int> vec) {
if (root == NULL) return;
if (!root->left && !root->right && targetSum == root->val) {
vec.push_back(root->val);
result.push_back(vec);
}
if (!root->left && !root->right && targetSum != root->val) return;
if (root->left) {
vec.push_back(root->val);
targetSum -= root->val;
findPath(root->left, targetSum, vec);
vec.pop_back();
targetSum += root->val;
}
if (root->right) {
vec.push_back(root->val);
targetSum -= root->val;
findPath(root->right, targetSum, vec);
vec.pop_back();
targetSum += root->val;
}
}
vector<vector<int>> pathSum(TreeNode* root, int targetSum) {
findPath(root, targetSum, vec);
return result;
}
};
// 优化版本, 可以不用传入vec
class Solution {
public:
vector<vector<int>> result;
vector<int> vec;
void findPath(TreeNode* root, int targetSum) {
if (root == NULL) return;
if (!root->left && !root->right && targetSum == root->val) {
vec.push_back(root->val);
result.push_back(vec);
vec.pop_back();
}
if (!root->left && !root->right && targetSum != root->val) return;
if (root->left) {
vec.push_back(root->val);
targetSum -= root->val;
findPath(root->left, targetSum);
targetSum += root->val;
vec.pop_back();
}
if (root->right) {
vec.push_back(root->val);
targetSum -= root->val;
findPath(root->right, targetSum);
targetSum += root->val;
vec.pop_back();
}
return;
}
vector<vector<int>> pathSum(TreeNode* root, int targetSum) {
result.clear();
vec.clear();
findPath(root, targetSum);
return result;
}
};
随想录方法:
递归
class solution {
private:
vector<vector<int>> result;
vector<int> path;
// 递归函数不需要返回值,因为我们要遍历整个树
void traversal(TreeNode* cur, int count) {
if (!cur->left && !cur->right && count == 0) { // 遇到了叶子节点且找到了和为sum的路径
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 ;
}
public:
vector<vector<int>> pathSum(TreeNode* root, int sum) {
result.clear();
path.clear();
if (root == NULL) return result;
path.push_back(root->val); // 把根节点放进路径
traversal(root, sum - root->val);
return result;
}
};
递归过程中当需要记录一些值,这些值在遍历的过程中发生变化,就一定要注意回溯的写法。
给定两个整数数组 inorder
和 postorder
,其中 inorder
是二叉树的中序遍历, postorder
是同一棵树的后序遍历,请你构造并返回这颗 二叉树 。
示例 1:
!https://assets.leetcode.com/uploads/2021/02/19/tree.jpg
输入:inorder = [9,3,15,20,7], postorder = [9,15,7,20,3]
输出:[3,9,20,null,null,15,7]
思考:简单的说,就是给定中序遍历和后续遍历,去构造这颗二叉树。自己想的步骤→ 先从后续遍历取到末尾的值,再到中序遍历中找到该值,根据该值将其分为左右两个部分。然后根据中序的两个区间再将后续分成两个区间。然后重复该步骤。
随想录:思路差不多。以 后序数组的最后一个元素为切割点,先切中序数组,根据中序数组,反过来再切后序数组。一层一层切下去,每次后序数组最后一个元素就是节点元素。
来看一下一共分几步:
class Solution {
private:
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 delimiterIndex;
for (delimiterIndex = 0; delimiterIndex < inorder.size(); delimiterIndex++) {
if (inorder[delimiterIndex] == rootValue) break;
}
vector<int> leftInorder(inorder.begin(), inorder.begin() + delimiterIndex);
vector<int> rightInorder(inorder.begin() + delimiterIndex + 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;
}
public:
TreeNode* buildTree(vector<int>& inorder, vector<int>& postorder) {
if (inorder.size() == 0 || postorder.size() == 0) return NULL;
return traversal(inorder, postorder);
}
};
看起来真的很复杂,做题的时候需要仔细判断边界条件,最好都用某一种边界条件。
思路清晰, 代码写出来一定是各种问题
对于调代码,阔以用日志的方法来搞清楚细节
https://programmercarl.com/0106.从中序与后序遍历序列构造二叉树.html#思路