1.基本思路
例如要构造如下二叉树:
已知:
前序序列preorder=9 3 5 6 7 1 2
中序序列inorder=5 3 7 6 9 1 2
或者
中序序列inorder=5 3 7 6 9 1 2
后序序列postorder=5 7 6 3 2 1 9
我们如何构造还原二叉树呢?
以前序序列和中序序列为例:
①首先通过前序遍历获得根结点root。
②根据根结点来划分中序序列。
③已知前序序列和中序序列长度一致,因此就可以来划分前序序列。
④分别获得了左子树的前序序列、中序序列和右子树的前序序列、中序序列。
⑤递归。
具体例子:
前序序列preorder=9 3 5 6 7 1 2
中序序列inorder=5 3 7 6 9 1 2
①根:9
②划分中序序列:
左边部分:5 3 7 6
右边部分:1 2
③划分前序序列:
左边部分:3 5 6 7长度和中序序列左边部分一致
右边部分:1 2
④左子树的前序序列、中序序列:3 5 6 7 和 5 3 6 7
右子树的前序序列、中序序列: 1 2 和 1 2
图示:
2.前序序列和中序序列构造二叉树
class Solution {
public:
TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder) {
if (inorder.size() == 0 || preorder.size() == 0) {
return nullptr;
}
TreeNode* root = new TreeNode();
root->val = preorder[0];
int splitPoint = 0;//分割点
for (int i = 0; i < inorder.size(); i++) {
if (inorder[i] == root->val) {
splitPoint = i;
break;
}
}
vector<int> inleftorder(inorder.begin(),inorder.begin() + splitPoint); //中序序列左序列
vector<int> inrightorder(inorder.begin() + splitPoint + 1,inorder.end()); //中序序列右序列
vector<int> preleftorder(preorder.begin()+1,preorder.begin() + inleftorder.size()+1); //前序序列左序列
vector<int> prerightorder(preorder.begin() + inleftorder.size()+1,preorder.end()); //前序序列右序列
root->left = buildTree(preleftorder, inleftorder); //左子树的前序序列和中序序列
root->right = buildTree(prerightorder, inrightorder); //右子树的前序序列和中序序列
return root;
}
};
3.中序序列和后序序列构造二叉树
class Solution {
public:
TreeNode* buildTree(vector<int>& inorder, vector<int>& postorder) {
if(inorder.size()==0 || postorder.size()==0){
return nullptr;
}
TreeNode* root = new TreeNode();
root->val=postorder[postorder.size()-1];
int inleft=0;//分割点
for(int i=0;i<inorder.size();i++){
if(inorder[i]==root->val){
inleft=i;
break;
}
}
postorder.resize(postorder.size()-1);//后序遍历序列减1
vector<int> inleftorder(inorder.begin(),inorder.begin()+inleft);//左子树中序序列
vector<int> inrightorder(inorder.begin()+inleft+1,inorder.end());//右子树中序序列
vector<int> postleftorder(postorder.begin(),postorder.begin()+inleftorder.size());//左子树后序序列
vector<int> postrightorder(postorder.begin()+inleftorder.size(),postorder.end());//右子树后序序列
root->left=buildTree(inleftorder,postleftorder);
root->right=buildTree(inrightorder,postrightorder);
return root;
}
};
4.总结
构造二叉树的过程,如何正确的写出各个子序列的边界是有些困难的,需要在稿纸上认真分析整个过程,确保边界不出错。两种构造方式代码略有不同,对于前序序列的处理,每次递归时都会往后移动一步,因为根结点已经处理过了,避免重复处理。
vector<int> preleftorder(preorder.begin() + 1,preorder.begin() + inleftorder.size() + 1); //后移一步
vector<int> prerightorder(preorder.begin() + inleftorder.size() + 1,preorder.end());
后序序列的根结点处理是通过修改后序序列的长度,避免重复处理根结点。
postorder.resize(postorder.size()-1);//后序遍历序列减1
最后,假如已知前序序列和后序序列可以唯一还原出二叉树吗,答案是否定的,因为虽然可以确定根结点但无法分出左右子树,会出现不同的二叉树结果。
例如下图: