题面
【题目描述】
给出二叉树的先序遍历和中序遍历,求后序遍历。
【输入】
输入共两行,第一行一个字符串,表示树的先序遍历,第二行一个字符串,表示树的中序遍历。树的结点一律用小写字母表示。字符串长度小于
100
100
100。
【输出】
输出仅一行,表示树的后序遍历序列。
【样例输入】
abdec
dbeac
【样例输出】
debca
算法分析
二叉树有三种遍历方法:
先序遍历:(1)访问根节点;(2)先序遍历左子树;(3)先序遍历右子树。
中序遍历:(1)中序遍历左子树;(2)访问根节点;(3)中序遍历右子树。
后序遍历:(1)后序遍历左子树;(2)后序遍历右子树;(3)访问根节点。
已知先序遍历和中序遍历:
对于先序遍历,第一个结点为根节点
a
a
a。接下来找出中序遍历的根结点,根据中序遍历的顺序,可以知道,根节点
a
a
a左边为
a
a
a的左子树,右边为
a
a
a的右子树:
对于先序遍历,根节点
a
a
a后紧跟的为
a
a
a的左子树,左子树访问完之后为
a
a
a的右子树。可以知道,
a
a
a的左子树的结点数目是固定的,那么可以通过中序遍历根节点
a
a
a左边的结点数目,即左子树的结点总数,从而在先序遍历中找到左子树的区间,对于样例,先序遍历中,根节点
a
a
a后
3
3
3个结点为
a
a
a的左子树,剩下的为右子树。
因此左子树的先序遍历为:
b
d
e
bde
bde,左子树的中序遍历为:
d
b
e
dbe
dbe;
右子树的先序遍历为:
c
c
c,左子树的中序遍历为:
c
c
c;
根结点为
a
a
a。
按照同样的方法,继续对左右子树进行拆分。
假设先序遍历的下标区间为
l
1
l_1
l1~
r
1
r_1
r1,中序遍历的下标区间为
l
2
l_2
l2 ~
r
2
r_2
r2:
先序的第一个结点
S
1
[
l
1
]
S_1[l_1]
S1[l1]为根,在中序遍历
S
2
S_2
S2中找到结点
S
1
[
l
1
]
S_1[l_1]
S1[l1],设位置为
m
m
m。
中序遍历中,根的左子树在中序的位置下标为为
l
2
l_2
l2~
m
−
1
m-1
m−1,右子树为
m
+
1
m+1
m+1~
r
2
r_2
r2。
先序访问根结点后紧跟访问左子树,之后为右子树
因此,中序遍历的左子树下标
l
2
l_2
l2~
m
−
1
m-1
m−1总共(
m
−
l
2
m-l_2
m−l2)个结点,对应先序遍历的
l
1
+
1
l_1+1
l1+1开始的(
m
−
l
2
m-l_2
m−l2)结点,即先序遍历的左子树下标区间为
l
1
+
1
l_1+1
l1+1 ~
l
1
+
m
−
l
2
l_1+m-l_2
l1+m−l2
中序遍历的右子树区间
m
+
1
m+1
m+1~
r
2
r_2
r2对应先序的右子树区间
l
1
+
m
+
l
2
+
1
l_1+m+l_2+1
l1+m+l2+1 ~
r
1
r_1
r1
根据后序遍历的访问顺序,左右子树遍历完毕,就可以输出根结点
S
1
[
l
1
]
S_1[l_1]
S1[l1]。
参考程序
#include<iostream>
#include<cstring>
using namespace std;
string a,b;
void dg(int x1,int y1,int x2,int y2) //先序遍历x1~y1,对应中序遍历x2~y2
{
if(x1>y1) return;
int root=b.find(a[x1]); //string自带的查找函数,找到a[x1]位置,返回下标
dg(x1+1,x1+root-x2,x2,root-1);
dg(x1+root-x2+1,y1,root+1,y2);
cout<<a[x1];
}
int main()
{
cin>>a>>b;
dg(0,a.length()-1,0,b.length()-1);
cout<<endl;
return 0;
}