目录
106. 从中序与后序遍历序列构造二叉树:
问题描述:
实现代码与解析:
切割法(递归):
原理思路:
索引版本:
105. 从前序与中序遍历序列构造二叉树:
问题描述:
实现代码与解析:
切割法(递归):
原理思路:
索引版本:
106. 从中序与后序遍历序列构造二叉树:
问题描述:
给定两个整数数组 inorder
和 postorder
,其中 inorder
是二叉树的中序遍历, postorder
是同一棵树的后序遍历,请你构造并返回这颗 二叉树 。
示例 1:
输入:inorder = [9,3,15,20,7], postorder = [9,15,7,20,3] 输出:[3,9,20,null,null,15,7]
示例 2:
输入:inorder = [-1], postorder = [-1] 输出:[-1]
实现代码与解析:
切割法(递归):
class Solution {
public:
TreeNode* buildTree(vector<int>& inorder, vector<int>& postorder)
{
//若为空树
if(postorder.size()==0)
{
return NULL;//返回NULL,最后的叶子结点左右子树都为空
}
int rootValue=postorder[postorder.size()-1];//后序最后一个元素
TreeNode* root=new TreeNode(rootValue);//后序遍历的第一个结点为根结点
//叶子结点
if(postorder.size()==1) return root;
int index;//中序切割点
//寻找切割点
for(index=0;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.pop_back();//去除后序最后一个元素
//切割后序数组
vector<int> leftPostorder(postorder.begin(),postorder.begin()+leftInorder.size());
vector<int> rightPostorder(postorder.begin()+leftInorder.size(),postorder.end());
root->left=buildTree(leftInorder,leftPostorder);
root->right=buildTree(rightInorder,rightPostorder);
return root;
}
};
原理思路:
用中序和后序、中序和前序构造二叉树是数据结构中的一种经典题型,如果只是画出二叉树的图像,大家应该是都会的,这里介绍一些如何用代码来还原二叉树。
1、首先写递归的终止条件,判断后序数组是否为空,若为空,说明该树是空树,直接返回NULL,同时也解决了递归式时有一侧子树为空的空结点可以返回。
//若为空树
if(postorder.size()==0)
{
return NULL;//返回NULL,最后的叶子结点左右子树都为空
}
2、判断是否进入到了叶子结点,也就是后序数组只剩下一个数,返回此结点,去掉此代码也是可以的,因为我们前面有了遇到空结点时返回的代码,遇到叶子结点再向下递归一层到空结点返回也是可以的。
//叶子结点
if(postorder.size()==1) return root;
3、然后我们就记录后序数组最后一个元素来寻找切割点了,把中序数组切割成左中序和右中序,然后再根据左中序大小,将后序数组切成左后序和右后序,注意在切割后序数组前把后序数组最后一个数去掉,因为我们这层已经用过这个结点值了,还有注意切割的左右是开还是闭,切割中序和后序时最好保持统一。
//切割中序数组
vector<int> leftInorder(inorder.begin(),inorder.begin()+index);
vector<int> rightInorder(inorder.begin()+index+1,inorder.end());
postorder.pop_back();//去除后序最后一个元素
//切割后序数组
vector<int> leftPostorder(postorder.begin(),postorder.begin()+leftInorder.size());
vector<int> rightPostorder(postorder.begin()+leftInorder.size(),postorder.end());
4、递归同时进行结点的连接,左子树传入左中序和左后序,右子树传入右中序和右后序。
root->left=buildTree(leftInorder,leftPostorder);
root->right=buildTree(rightInorder,rightPostorder);
还有另一种写法,就是每次递归时多传入切割的中序区间的索引,后序区间的索引的参数。这样就不用我们每次递归时在单独创建vector了,减少了空间,不过原理是相同的,下面给出:
索引版本:
class Solution {
private:
TreeNode* traversal (vector<int>& inorder, int inorderBegin, int inorderEnd, vector<int>& postorder, int postorderBegin, int postorderEnd)
{
if (postorderBegin == postorderEnd) return NULL;
int rootValue = postorder[postorderEnd - 1];
TreeNode* root = new TreeNode(rootValue);
if (postorderEnd - postorderBegin == 1) return root;
int Index;//中序切割点
for (Index = inorderBegin; Index < inorderEnd;Index++)
{
if (inorder[Index] == rootValue) 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;
}
public:
TreeNode* buildTree(vector<int>& inorder, vector<int>& postorder) {
if (inorder.size() == 0 || postorder.size() == 0) return NULL;
return traversal(inorder, 0, inorder.size(), postorder, 0, postorder.size());
}
};
105. 从前序与中序遍历序列构造二叉树:
问题描述:
给定两个整数数组 preorder
和 inorder
,其中 preorder
是二叉树的先序遍历, inorder
是同一棵树的中序遍历,请构造二叉树并返回其根节点。
示例 1:
输入: preorder = [3,9,20,15,7], inorder = [9,3,15,20,7] 输出: [3,9,20,null,null,15,7]
示例 2:
输入: preorder = [-1], inorder = [-1] 输出: [-1]
实现代码与解析:
切割法(递归):
class Solution {
public:
TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder)
{
if(preorder.size()==0) return NULL;
int rootValue=preorder[0];
TreeNode* root=new TreeNode(rootValue);
//叶子结点
if(preorder.size()==1) return root;
int index;//切割点
for(index=0;index<inorder.size();index++)
{
if(rootValue==inorder[index]) 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=buildTree(leftPreorder,leftInorder);
root->right=buildTree(rightPreorder,rightInorder);
return root;
}
};
原理思路:
此题与上一题原理相同,就不详细解释了,还是给出索引版本的代码:
索引版本:
class Solution {
private:
TreeNode* traversal (vector<int>& inorder, int inorderBegin, int inorderEnd, vector<int>& preorder, int preorderBegin, int preorderEnd) {
if (preorderBegin == preorderEnd) return NULL;
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;
}
public:
TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder)
{
return traversal(inorder, 0, inorder.size(), preorder, 0, preorder.size());
}
};