一、相关题目链接
zoj:1944 Tree Recovery
hdu:1710 Binary Tree Traversals
二、题意简述
输入一个二叉树的前序遍历和中序遍历序列,要求输出该二叉树的后序遍历序列。
三、解析
学过二叉树的同学应该知道,前序遍历顺序是根、左、右,中序遍历顺序是左、根、右,由此容易推导出,前序序列的第一个结点必定是整棵树的根。接下来从中序序列中寻找它的位置,由中序的特性,以该结点为分界线,其左侧的结点位于树的左子树上,而右侧的结点位于树的右子树上。
二叉树可以分解成若干个子树,这些子树都保留二叉树的特性,由此我们不难推出,上面的方法是可以递归的,即从前序序列中不断找到子树的根,再在中序序列以该子树根为界线中划分出子树的左右子树。
我们需要用两个数组接收前序中序遍历的字符串。先设置一个在前序中的指针root_i,它的作用是指明我们当前需要建立的根结点的值。通过left和right这两个参数来划分子树的范围,再对这个范围内的字符串进行遍历,直到在中序中找到结点值和root_i指向的值相同时候的下标i,此时,我们便可以建立新结点,同时使root_i后移。找这样一个i的原因是方便划分左右子树在中序字符串中的范围,左子树是(left,i-1),右子树是(i+1,right)。
递归到叶子结点时,left==right,再往下递归,会使下一层的left>right,所以以此作为递归结束的条件。同时,指针引用的特性保证开辟的结点不会跑丢。
四、代码
注:以ZOJ的题目要求为例
#include<iostream>
#include<cstring>
using namespace std;
const int MAX_N = 30;
char preor[MAX_N];//存储前序序列
char inor[MAX_N];//存储中序序列
int root_i=0;//在前序序列中指向根的指针
struct node//二叉树结点
{
char v;
node *l, *r;
node(char c = '0', node *le = NULL, node *ri = NULL) :v(c), l(le), r(ri) {}
};
void createTree(int left,int right,node *&root)
{
if(left>right)
return;
for(int i=left;i<=right;i++)
{
if(preor[root_i]==inor[i])//找到根
{
if(root==NULL)
{
root=new node(inor[i]);
root_i++;
createTree(left,i-1,root->l);
createTree(i+1,right,root->r);
}
}
}
}
void postOrder(node *&root)//后序遍历
{
if(root==NULL)
return;
postOrder(root->l);
postOrder(root->r);
printf("%c",root->v);
}
int main()
{
while(~scanf("%s",&preor))
{
scanf("%s",&inor);
node *tr=NULL;
createTree(0,strlen(inor)-1,tr);
postOrder(tr);
printf("\n");
root_i=0;
}
return 0;
}
因为是用于通过竞赛题目,所以我们可以不用考虑空间回收的问题。在实际应用中,程序结束前需要依次回收开辟的空间:
void remove(node *root)
{
if(root==NULL)
return;
remove(root->l);
remove(root->r);
delete root;
}