算法day15|513.找树左下角的值、112. 路径总和、113.路径总和Ⅱ、106.从中序与后序遍历序列构造二叉树、105.从前序与中序遍历序列构造二叉树
513.找树左下角的值
一开始题意理解错了,做了好多无用功…看来读题真的非常重要。以为重点是左下角,其实题目的中的左下角必须是在最后一层。即使最后一层没有左孩子结点,只有右孩子结点,那么这个右孩子也算是树的左下角。
迭代法
class Solution {
public:
int findBottomLeftValue(TreeNode* root) {
queue<TreeNode*> que;
que.push(root);
int result;
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;
}
};
迭代法我一开始也没有做出来,主要还是因为不知道该怎么去定位最后一层,我该凭什么逻辑来认定它就是最后一层呢?然后陷进去出不来了。而卡哥的解法非常巧妙:我们不管什么时候是最后一层,我先把一层里面第一个值赋给result。这样的话,第一层的第一个值赋给result,等到了的二层的时候机会自动更新为第二层的第一个值,第三层的时候以此类推… 所以当到了最后一层的时候,result就是最后一层的第一个值。只有对过程和结果的理解非常深刻才能想到这个。我们不单单看着最后一层,我们看到是整个逻辑过程。
另外,除非思路贴吧清晰,否则不要使用while(n–),而要改用for(int i=0;i<n;i++)
112. 路径总和
这题还是比较简单的,代码如下:
class Solution {
public:
void traversal(TreeNode*root,vector<int>&path,vector<int>& result)
{
path.push_back(root->val);
if(root->left==nullptr&&root->right==nullptr)
{
int count=0;
for(int i=0;i<path.size();i++)
count+=path[i];
result.push_back(count);
return;
}
if(root->left)
{
traversal(root->left,path,result);
path.pop_back();
}
if(root->right)
{
traversal(root->right,path,result);
path.pop_back();
}
}
bool hasPathSum(TreeNode* root, int targetSum) {
vector<int> path;
vector<int> result;
int flag=false;
if(root!=nullptr)
{
traversal(root,path,result);
for(int i=0;i<result.size();i++)
{
if(result[i]==targetSum)
flag=true;
}
}
return flag;
}
};
这题思路和257.二叉树的所有路径基本一致,只是把其中的一个字符串型数组改成了整数型数组,核心逻辑完全相同,属于模板题。
113.路径总和Ⅱ
这题同样很简单,只需要再加一个二位数组来接收一下路径即可。具体代码如下:
class Solution {
public:
void traversal(TreeNode*root,vector<int>&path,vector<int>& result,vector<vector<int>>&Path)
{
path.push_back(root->val);
if(root->left==nullptr&&root->right==nullptr)
{
Path.push_back(path);
int count=0;
for(int i=0;i<path.size();i++)
count+=path[i];
result.push_back(count);
return;
}
if(root->left)
{
traversal(root->left,path,result,Path);
path.pop_back();
}
if(root->right)
{
traversal(root->right,path,result,Path);
path.pop_back();
}
}
vector<vector<int>> pathSum(TreeNode* root, int targetSum) {
vector<int> path;
vector<int> result;
vector<vector<int>> Path;
vector<vector<int>> Result;
if(root!=nullptr)
{
traversal(root,path,result,Path);
for(int i=0;i<result.size();i++)
{
if(result[i]==targetSum)
Result.push_back(Path[i]);
}
}
return Result;
}
};
106.从中序与后序遍历序列构造二叉树
这题难度很大,而且其中的vector的用法我也是第一次见,一共花了一个多小时才弄明白,具体代码如下:
class Solution {
public:
TreeNode*traversal(vector<int>& inorder, vector<int>& postorder)
{
if(postorder.size()==0)
return nullptr;
int value=postorder[postorder.size()-1];
TreeNode*root=new TreeNode(value);
if(postorder.size()==1)
return root;
int index=0;
for(;index<inorder.size();index++)
{
if(inorder[index]==value)
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.empty())
return traversal(inorder,postorder);
else
return nullptr;
}
};
总体思路(递归):
-
参数显然是中序数组和后序数组,返回值是结点类型(这意味肯定有函数赋值的操作)
-
终止条件,有2个:一是当后续数组为空时(每递归依次后序数组大小都会减一),返回null,比较正常;二是当后续数组只有一个元素时,直接返回root;(可以理解成剪枝的操作)
-
单层递归:
-
首先,先序遍历,创造当前结点空间并赋值(从当前节点往下走,因为孩子的递归需要父节点的信息,如怎么去切割,所有要先序遍历)。
-
然后就是重头戏:为当前的左右孩子分别切割左右中序和左右后续。
- 先切割原来的中序数组。具体方法:定义两个vector数组,把左边的数组赋值到一个vector中,再把右边的数组赋值到另一个vector中;
- 然后再切割后续数组。具体方法:把前面的和左中序相同大小的数组赋值到一个vector中,再把剩余的数组赋值到另一个vector中(在切割需要将原后序数组的大小减一)
- 要注意,切割的时候要保证区间的统一,要么都是左开右闭,要不都是左闭右开等等,看情况选择一个,之后所有的区间都是按照这一个逻辑进行
-
切割完成之后,给左孩子分配左中序和左后序,给右孩子分配有右中序和右后序,递归
-
最后别忘了返回当前结点
-
105.从前序与中序遍历序列构造二叉树
class Solution {
public:
TreeNode*traversal(vector<int>& preorder, vector<int>& inorder)
{
if(preorder.size()==0)
return nullptr;
int value=preorder[0];
TreeNode*root=new TreeNode(value);
if(preorder.size()==1)
return root;
int index=0;
for(;index<inorder.size();index++)
{
if(inorder[index]==value)
break;
}
vector<int> leftInorder(inorder.begin(),inorder.begin()+index);
vector<int> rightInorder(inorder.begin()+index+1,inorder.end());
preorder.erase(preorder.begin());
vector<int> leftPreorder(preorder.begin(),preorder.begin()+leftInorder.size());
vector<int> rightPreorder(preorder.begin()+leftInorder.size(),preorder.end());
root->left=traversal(leftPreorder,leftInorder);
root->right=traversal(rightPreorder,rightInorder);
return root;
}
TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder) {
if(!preorder.empty())
return traversal(preorder,inorder);
else
return nullptr;
}
};
原理基本相同,唯一不同的是先序数组要先删除第一个元素,而前面一题是删除最后一个元素。即preorder.erase(preorder.begin())。此函数内部包含了删除之后的数组位置的重新布置以及size–