LeetCode 513 找树左下角的值
题目链接:https://leetcode.cn/problems/find-bottom-left-tree-value/
思路:
用到了深度这一二叉树属性。该题用任意一种遍历方式均可。因为本题不需要对中节点进行处理。
迭代法的关键在于终止条件。要理解题意,什么叫最底层的最左边。
代码:
递归法
/**
* 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 = 0;
int findBottomLeftValue(TreeNode* root) {
int depth = 0;
traversal(root,depth);
return result;
}
void traversal(TreeNode *node,int depth)
{
// 终止条件
if(!node->left&&!node->right)
{
if(depth>Maxdepth)
{
Maxdepth = depth;
result = node->val;
}
}
// 左
if(node->left)
{
// 记录深度的增大
depth++;
traversal(node->left,depth);
depth--; // 回溯的过程,根节点走完左边之后,可以回过头来走右边
}
// 右
if(node->right)
{
depth++;
traversal(node->right,depth);
depth--;
}
}
};
层序遍历
/**
* 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 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;
}
};
总结
想到层序遍历法的思路,但是没写出来。迭代法的话没想到思路,看了视频后可以理解题意,希望下次自己可以写出来。
路径总和
注意事项:
递归函数什么时候需要返回值?什么时候不需要返回值?这里总结如下三点:
如果需要搜索整棵二叉树且不用处理递归返回值,递归函数就不要返回值。(这种情况就是本文下半部分介绍的113.路径总和ii)
如果需要搜索整棵二叉树且需要处理递归返回值,递归函数就需要返回值。 (这种情况我们在236. 二叉树的最近公共祖先 (opens new window)中介绍)
如果要搜索其中一条符合条件的路径,那么递归一定需要返回值,因为遇到符合条件的路径了就要及时返回。(112.路径总和)
相关题目
LeetCode 112 路径总和
题目链接:https://leetcode.cn/problems/path-sum/
思路:
自己的思路:采用累加器,将路径上的节点和加起来,直到叶节点处,然后在叶节点处进行判断是否等于题目要求的和。注意:需要用回溯。
卡哥的思路:采用累减器,用题目要求的总和减去节点上的值,然后在叶节点处判断此时,和是否为0,若为0代表该路径符合题意。注意:需要回溯
代码:
自己的代码
/**
* 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:
bool hasPathSum(TreeNode* root, int targetSum) {
int sum = 0;
if(root==nullptr)
return false;
return traversal(root,sum,targetSum);
}
bool traversal(TreeNode *root,int sum,int targetSum)
{
sum += root->val;
if(!root->left&&!root->right)
{
if(sum==targetSum)
return true;
else
return false;
}
if(root->left)
{
// 此时递归里面的sum是复制赋值,不影响父节点的sum,所以其实已经回溯了
if(traversal(root->left,sum,targetSum))
return true;
// sum -= root->left->val;
}
if(root->right)
{
if(traversal(root->right,sum,targetSum))
return true;
// sum -= root->right->val;
}
return 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:
bool hasPathSum(TreeNode* root, int targetSum) {
if(root == nullptr) return false;
return traversal(root,targetSum-root->val);
}
bool traversal(TreeNode *node,int count)
{
// 终止条件
if(!node->left&&!node->right&&count == 0) return true;
if(!node->left&&!node->right) return false;
//左
if(node->left)
{
// 此处需要回溯是因为在将count放进去递归函数之前,先对count减掉了节点的值
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;
}
};
总结
第一时间想到了累加器的方法,也想到了要回溯,因为昨天做了求所有路径的题目。但是在回溯的过程中,还是没想明白要不要写出来。因为我的是复制赋值,所以它本身就带有回溯了。还是要多从减法方面思考,从而减少代码量。
LeetCode 113 路径总和ii
题目链接:https://leetcode.cn/problems/path-sum-ii/
思路:
和上一题思路基本一样,只不过该题要的是所有满足题意的路径,所以递归函数不需要返回值。
代码:
自己的代码
/**
* 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:
vector<vector<int>> pathSum(TreeNode* root, int targetSum) {
vector<vector<int>>result;
vector<int>path;
if(!root) return result;
traversal(root,path,result,targetSum-root->val);
return result;
}
void traversal(TreeNode *node,vector<int>&path,vector<vector<int>>&result,int count)
{
path.push_back(node->val);
if(!node->left&&!node->right&&count==0)
{
result.push_back(path);
return ;
}
if(!node->left&&!node->right)
return;
if(node->left)
{
count -= node->left->val;
traversal(node->left,path,result,count);
count += node->left->val; // 回溯
path.pop_back(); // 回溯
}
if(node->right)
{
count -= node->right->val;
traversal(node->right,path,result,count);
count += node->right->val; // 回溯
path.pop_back(); //回溯
}
}
};
总结
很开心自己可以通过之前学到的东西,自己把题目AC了。但是还是有小瑕疵,就是在回溯path的时候,要将path设为引用的,即函数的参数path是引用的,不然不需要特意加一行path.pop_back()的代码。
用前序和中序/中序和后序去构造二叉树
整体思想:
后序:左右中 中序:左中右 前序:中左右
以后序和中序构造二叉树为例:
理论基础:
将后序数组的最后一个元素为切割点,先切中序数组,根据中序数组,反过来再切后序数组。一层一层切下去,每次后序数组最后一个元素就是节点元素。注:前序的话就反过来,以第一个元素为切割点。
代码步骤:
第一步:如果数组大小为零的话,说明是空节点了。
第二步:如果不为空,那么取后序数组最后一个元素作为节点元素。
第三步:找到后序数组最后一个元素在中序数组的位置,作为切割点
第四步:切割中序数组,切成中序左数组和中序右数组 (顺序别搞反了,一定是先切中序数组)
第五步:切割后序数组,切成后序左数组和后序右数组
第六步:递归处理左区间和右区间
注意细节:
在切割中序和后序数组的时候,要保证循环不变量,即以左闭右闭还是左闭右开去左切割。切割中序和后序数组的循环不变量都需要保证一样。
中序数组大小一定是和后序数组的大小相同的。所以可以通过中序的左区间大小去切割后序数组。
相关题目
LeetCode 106 从中序与后序遍历序列构造二叉树
题目链接:https://leetcode.cn/problems/construct-binary-tree-from-inorder-and-postorder-traversal/
代码:
思路清晰版
/**
* 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* buildTree(vector<int>& inorder, vector<int>& postorder) {
if(inorder.empty()||postorder.empty()) return nullptr;
return traversal(inorder,postorder);
}
TreeNode* traversal(vector<int> &inorder,vector<int> &postorder)
{
// 第一步
// 确定是否为空节点
if(postorder.size()==0) return nullptr;
// 第二步
// 确定节点元素
int rootval = postorder[postorder.size()-1];
TreeNode *root = new TreeNode(rootval);
// 处理叶子节点
if(postorder.size()==1) return root;
// 第三步
// 找到切割点
int index = 0;
for(index;index<inorder.size();index++)
{
if(inorder[index]==rootval)
break;
}
// 第四步
// 左闭右开原则
// 切割中序数组
vector<int>leftinorder = vector<int>(inorder.begin(),inorder.begin()+index);
vector<int>rightinorder = vector<int>(inorder.begin()+index+1,inorder.end()); // +1的作用是为了去掉节点元素
// 第五步
// 左闭右开原则
// 切割后序数组
postorder.resize(postorder.size()-1); // 先将节点元素排除掉
vector<int>leftpostorder = vector<int>(postorder.begin(),postorder.begin()+leftinorder.size());
vector<int>rightpostorder = vector<int>(postorder.begin()+leftinorder.size(),postorder.end());
// 第六步
// 递归处理左孩子和右孩子
root->left = traversal(leftinorder,leftpostorder);
root->right = traversal(rightinorder,rightpostorder);
return root;
}
};
优化版
/**
* 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* buildTree(vector<int>& inorder, vector<int>& postorder) {
if(inorder.empty()||postorder.empty()) return nullptr;
return traversal(inorder,0,inorder.size(),postorder,0,postorder.size());
}
TreeNode* traversal(vector<int> &inorder,int inorderBegin,int inorderEnd,vector<int> &postorder,int postorderBegin,int postorderEnd)
{
// 第一步
// 确定是否为空节点
if(postorderBegin == postorderEnd) return nullptr;
// 第二步
// 确定节点元素
int rootval = postorder[postorderEnd-1];
TreeNode *root = new TreeNode(rootval);
// 处理叶子节点
if(postorderEnd-postorderBegin==1) return root;
// 第三步
// 找到切割点
int index = inorderBegin;
for(index;index<inorderEnd;index++)
{
if(inorder[index]==rootval)
break;
}
// 第四步
// 左闭右开原则
// 切割中序数组
int leftinorderBegin = inorderBegin;
int leftinorderEnd = index;
int rightinorderBegin = index+1;
int rightinorderEnd = inorderEnd;
// 第五步
// 左闭右开原则
// 切割后序数组
int leftpostorderBegin = postorderBegin;
int leftpostorderEnd = postorderBegin+index-inorderBegin;
int rightpostorderBegin = postorderBegin+(index-inorderBegin);
int rightpostorderEnd = postorderEnd-1; // 要排除最后一个元素
// 第六步
// 递归处理左孩子和右孩子
root->left = traversal(inorder,leftinorderBegin,leftinorderEnd,postorder,leftpostorderBegin,leftpostorderEnd);
root->right = traversal(inorder,rightinorderBegin,rightinorderEnd,postorder,rightpostorderBegin,rightpostorderEnd);
return root;
}
};
LeetCode 105 从前序与中序遍历序列构造二叉树
题目链接:https://leetcode.cn/problems/construct-binary-tree-from-preorder-and-inorder-traversal/
代码:
思路清晰版
/**
* 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* buildTree(vector<int>& preorder, vector<int>& inorder) {
if(preorder.empty()||inorder.empty()) return nullptr;
return traversal(preorder,inorder);
}
TreeNode* traversal(vector<int>&preorder,vector<int>&inorder)
{
// 第一步
// 处理空节点
if(preorder.size()==0) return nullptr;
// 第二步
// 寻找节点元素
int rootval = preorder[0];
TreeNode *root = new TreeNode(rootval);
// 处理叶子节点
if(preorder.size()==1) return root;
// 第三步
// 找切割点
int index = 0;
for(index;index<inorder.size();index++)
{
if(inorder[index]==rootval)
break;
}
// 第四步
// 切割中序数组
vector<int>leftinorder(inorder.begin(),inorder.begin()+index);
vector<int>rightinorder(inorder.begin()+index+1,inorder.end());
// 第五步
// 切割前序数组
vector<int>leftpreorder(preorder.begin()+1,preorder.begin()+index+1);
vector<int>rightpreorder(preorder.begin()+index+1,preorder.end());
// 第六步
// 处理左子树和右子树的递归
root->left = traversal(leftpreorder,leftinorder);
root->right = traversal(rightpreorder,rightinorder);
return root;
}
};
优化版
/**
* 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* buildTree(vector<int>& preorder, vector<int>& inorder) {
if(preorder.empty()||inorder.empty()) return nullptr;
return traversal(preorder,0,preorder.size(),inorder,0,inorder.size());
}
TreeNode* traversal(vector<int> &preorder,int preorderBegin,int preorderEnd,vector<int> &inorder,int inorderBegin,int inorderEnd)
{
// 第一步
// 处理空节点
if(preorderBegin==preorderEnd) return nullptr;
// 第二步
// 确定节点元素
int rootval = preorder[preorderBegin];
TreeNode *root = new TreeNode(rootval);
// 第三步
// 确定中序数组切割点
int index = inorderBegin;
for(index;index<inorderEnd;index++)
{
if(inorder[index]==rootval)
break;
}
// 第四步
// 切割中序数组
int leftinorderBegin = inorderBegin;
int leftinorderEnd = index; // 此处直接是index的原因:因为在上面是从inorderBegin开始找index的值的
int rightinorderBegin = leftinorderEnd+1;
int rightinorderEnd = inorderEnd;
// 第五步
// 切割前序数组
int leftpreorderBegin = preorderBegin+1;
int leftpreorderEnd = preorderBegin+1+index-inorderBegin;
int rightpreorderBegin = leftpreorderEnd;
int rightpreorderEnd = preorderEnd;
// // 以下为日志
// cout << "----------" << endl;
// cout << "leftInorder :";
// for (int i = leftinorderBegin; i < leftinorderEnd; i++) {
// cout << inorder[i] << " ";
// }
// cout << endl;
// cout << "rightinorder :";
// for (int i = rightinorderBegin; i < rightinorderEnd; i++) {
// cout << inorder[i] << " ";
// }
// cout << endl;
// cout << "leftpreorder :";
// for (int i = leftpreorderBegin; i < leftpreorderEnd; i++) {
// cout << preorder[i] << " ";
// }
// cout << endl;
// cout << "rightpreorder :";
// for (int i = rightpreorderBegin; i < rightpreorderEnd; i++) {
// cout << preorder[i] << " ";
// }
// cout << endl;
// cout<<"leftinorderBegin = "<<leftinorderBegin<<endl;
// cout<<"leftinorderEnd = "<<leftinorderEnd<<endl;
// cout<<"rightinorderBegin = "<<rightinorderBegin<<endl;
// cout<<"rightinorderEnd = "<<rightinorderEnd<<endl;
// cout<<"leftpreorderBegin = "<<leftpreorderBegin<<endl;
// cout<<"leftpreorderEnd = "<<leftpreorderEnd<<endl;
// cout<<"rightpreorderBegin = "<<rightpreorderBegin<<endl;
// cout<<"rightpreorderEnd = "<<rightpreorderEnd<<endl;
// 第六步
// 左子树和右子树的递归处理
root->left = traversal(preorder,leftpreorderBegin,leftpreorderEnd,inorder,leftinorderBegin,leftinorderEnd);
root->right = traversal(preorder,rightpreorderBegin,rightpreorderEnd,inorder,rightinorderBegin,rightinorderEnd);
return root;
}
};
总结
一开始看视频学习了如何从后序与中序遍历序列构造二叉树。写完整版代码的时候可以写下来,但是在写优化版代码的时候,对于切割中序数组和后序数组那里总是会出现溢出的问题。后面在写从前序与中序遍历序列构造二叉树中,也是完整代码可以写出,优化版的代码还是会有溢出问题。需要多加练习。
今日总结:
今天的题目加强了递归和回溯的练习,开始从二叉树的属性过渡到构造二叉树。自己今天也写出了回溯的代码,很开心。说明自己还是有一直在进步的。继续加油!