两个遍历确定一棵树,其中必须有一个是中序遍历。
现在有一个问题,已知二叉树的前序遍历和中序遍历:
PreOrder: GDAFEMHZ
InOrder: ADEFGHMZ
我们如何还原这颗二叉树,并求出他的后序遍历?
我们基于一个事实:中序遍历一定是 { 左子树中的节点集合 },root,{ 右子树中的节点集合 },前序遍历的作用就是找到每颗子树的root位置。
输入:前序遍历,中序遍历
1、寻找树的root,前序遍历的第一节点G就是root。
2、观察前序遍历GDAFEMHZ,知道了G是root,剩下的节点必然在root的左或右子树中的节点。
3、观察中序遍历ADEFGHMZ。其中root节点G左侧的ADEF必然是root的左子树中的节点,G右侧的HMZ必然是root的右子树中的节点,root不在中序遍历的末尾或开始就说明根节点的两颗子树都不为空。
4、观察左子树ADEF,按照前序遍历的顺序来排序为DAFE,因此左子树的根节点为D,并且A是左子树的左子树中的节点,EF是左子树的右子树中的节点。
5、同样的道理,观察右子树节点HMZ,前序为MHZ,因此右子树的根节点为M,左子节点H,右子节点Z。
观察发现,上面的过程是递归的。先找到当前树的根节点,然后划分为左子树,右子树,然后进入左子树重复上面的过程,然后进入右子树重复上面的过程。最后就可以还原一棵树了:
接着借助一个二维数组,和一个height变量(记录当前所在的层数)就可以在递归的过程中按层把树存起来了。
同理,由后序遍历,中序遍历得到前序遍历和按层输出的方法思路一样,过程如下图:
代码如下:
#include<bits/stdc++.h>
using namespace std;
/*struct TreeNode
{
char val;
TreeNode *left,*right;
TreeNode(char c=' '):
val(c),left(nullptr),right(nullptr){}
};*/
void getPostorder(const string&,const string&,string &,int);
void getPreorder(const string &,const string &,string &,int );
void getLevel(const string&,const string&,int,vector<vector<char>>&,int);
string Preorder="GDAFEMHZ";
string Inorder="ADEFGHMZ";
int main()
{
string Postorder="";
string new_Pre="";
getPostorder(Preorder,Inorder,Postorder,int(Preorder.size()));
getPreorder(Postorder,Inorder,new_Pre,int(Inorder.size()));
if(Preorder==new_Pre)
cout<<"Pre: \n"<<new_Pre<<endl;
else
cout<<"wrong~"<<endl;
vector<vector<char>>level;
getLevel(Preorder,Inorder,int(Preorder.size()),level,0);
for(auto i:level){
for(auto j:i)
cout<<j<<" ";
cout<<endl;
}
return 0;
}
//根据先序,中序,得出后序。这里可以一并完成按层存储的过程,但是由于参数过多,方便理解将两个分为两个函数。
void getPostorder(const string &Pre,const string &In,string &Pos,int len)
{
if(len==0)
return;
char c=Pre[0];
//在中序遍历中找根节点
int root_index=0;
for(;root_index<len;++root_index){
if(In[root_index]==c)
break;
}
//左子树
getPostorder(Pre.substr(1),In,Pos,root_index);
//右子树
getPostorder(Pre.substr(root_index+1),In.substr(root_index+1),Pos,len-root_index-1);
Pos.insert(Pos.end(),c);
return;
}
//根据后序,中序,得出先序
void getPreorder(const string &Post,const string &In,string &Pre,int len)
{
if(len==0)
return;
char c=Post[Post.size()-1];
//在中序中寻找根节点
int root_index=0;
for(;root_index<len;++root_index){
if(In[root_index]==c)
break;
}
//先序,就先执行插入字符的操作
Pre.insert(Pre.end(),c);
//左子树
getPreorder(Post.substr(0,root_index),In,Pre,root_index);
//右子树
getPreorder(Post.substr(root_index,len-root_index-1),In.substr(root_index+1),
Pre,len-root_index-1);
return;
}
//根据先序,中序,按层打印出树,按后序和中序就不再重复了
void getLevel(const string &Pre,const string &In,int len,vector<vector<char>>&level,int height)
{
if(len==0)
return;
char c=Pre[0];
if(height>=level.size())
level.emplace_back(vector<char>());
level[height++].emplace_back(c);
//在中序遍历中找根节点
int root_index=0;
for(;root_index<len;++root_index){
if(In[root_index]==c)
break;
}
//左子树
getLevel(Pre.substr(1),In,root_index,level,height);
//右子树
getLevel(Pre.substr(root_index+1),In.substr(root_index+1),len-root_index-1,level,height);
return;
}