力扣513、找树左下角的值
分析:这里首先明确本题所说的树的左下角值的含义,它指的是二叉树中最后一行,最左侧节点的数值,因此首先要保证该节点是最后一行才可以,其次要找最后一行的第一个节点。那么显然,用层序遍历来解决的话是比较简单且非常合适的。
代码:
/**
* 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 traversal(TreeNode* root) {
//层序遍历写法
//假设二叉树至少有一个节点
queue<TreeNode*>que;
int result;
que.push(root);
while(!que.empty()){
int size=que.size();
for(int i=0;i<size;i++){
TreeNode* cur=que.front();
if(i==0)result=cur->val;
que.pop();
if(cur->left)que.push(cur->left);
if(cur->right)que.push(cur->right);//空节点不入栈
}
}
return result;
}
int findBottomLeftValue(TreeNode* root) {
return traversal(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:
//递归法相当于把整个二叉树都给遍历了一遍
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;
}
};
112、路径总和
思路:要求一条路径的和是不是等于所给的目标值,那么最直接的想法当然就是不断累加一条路径上节点的值看看能不能得到目标值了,那么这里有一个问题,就是应该采用怎样的方式来累加,即采用什么样的顺序去累加,其实我们分析一下就会发现,既然本题不涉及对所访问节点的处理,那么用什么遍历顺序(前中后序)实际上都是可以的。
为了解决问题的方便,我这里采用累计减小的方式来写代码,这样当目标值被减为0的时候,就找到了目标路径,返回true,若一条路径遍历完(已经遍历到叶子节点),目标值仍不为0,就要换条路去尝试,此时就要把之前减去的值再加上,也就是说本题也用到了回溯的思想。如果遍历完所有路径都没有找到想要的结果,返回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 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);
}
};
106、从中序和后序遍历构造二叉树
思路:本题的关键是要弄清楚中序和后序的作用,中序数组的作用是区分左右子树,后序数组的作用是确定根节点,具体实现的话,首先肯定要有根节点才能去划分呀,所以第一步要先去后序数组找根节点,根据左右中的原则可以知道,在后序数组中,它的最后一个元素就是树的根节点,这里要注意一下,在找根节点之前要先确定中序数组或后序数组不为空,若为空的话直接返回空就好了,若不为空的话再去找根节点,并通过new运算在堆区建立出根节点(如果该节点是叶子节点,直接返回该节点)。第二步要拿这个根节点去中序数组中去划分左右区间,也就是寻找切割点,通过一个for循环来遍历中序数组,查找元素值等于根节点元素值的元素所在的下标,把他保存起来(因此切割点下标的定义要放在for循环外面)。第三步要拿这个下标去切割中序数组,由于中序数组是按照左中右的方式排列的,因此该下标左边的区间中的元素就是左子树的元素,同理可以找到右子树。第四步,不管中序也好后序也罢,对于同一个根节点,它们左右子树的大小都是一样的,因此可以拿左子树的大小去切割后序数组,因为后序数组是按照左右中的方式来排列的,因此该方法是可行的,注意在切割前要把根节点去掉,该节点已经处理过了。第五步,递归的处理左右区间即可。(左区间就是传入左中序左后序,右区间同理),最后返回根节点,二叉树就构造完毕了。
代码:
/**
* 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* traversal(vector<int>& inorder,vector<int>& postorder){
//若根节点为空,即数组大小为0时,返回空
if(inorder.size()==0)return nullptr;
//不空,先取后序数组最后一个元素做根节点
int rootValue=postorder[postorder.size()-1];
TreeNode* root=new TreeNode(rootValue);
//如果该节点是叶子节点,直接返回
if(postorder.size()==1)return root;
//拿最后一个节点去中序数组找切割点
int index=0;
for(;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 nullptr;
return traversal(inorder, postorder);
}
};
补充说明:栈区内存虽然操作速度快,但是大小收到限制,堆区的数据则更加灵活,可分配的空间也更大,由程序员手动分配和释放,更适合于这种动态分配的情况。