题目描述
给出一棵二叉树的中序与后序排列。求出它的先序排列。(约定树结点用不同的大写字母表示,长度≤8)。
输入格式
2行,均为大写字母组成的字符串,表示一棵二叉树的中序与后序排列。
输出格式
1行,表示一棵二叉树的先序。
输入输出样例
输入
BADC
BDCA
输出
ABCD
问题解析
本题很明显是设法将后序序列和中序序列先还原成二叉树结构,然后再输出先序序列的二叉树。很自然的一个想法是通过递归求出二叉树的左右子树,进而得到完整的二叉树。
已知后序序列的最后一项是二叉树的根节点,不妨设为A,在中序序列中A的左边是二叉树的左子树,A的右边是右子树。之后,我们可以将二叉树分为左右两部分(也就是中序序列中A的左右两边),依据后序序列分别找到左右两部分的根节点,即可重复次过程,完成本题。
但是如何找到左右部分的根节点呢?我观察良久,找到一个方法:
对于后序序列中的根节点A,设其位置为g(A),假设其对应到中序序列的位置为f(A)。将后序序列从右往左看,对于第一个节点B,若满足g(B) < g(A),则B必然为左子树的根节点。同理,对于第一个满足g( C ) > g(A)的节点C,C必然为右子树的根节点。之后,用同样的方法可以完成递归。
计算方法
对于本算法的实现,我提供两个思路:一个需要构造二叉树,一个不用。按需阅读即可。
思路一
这是一个很自然的思路,但是有点麻烦(麻烦在于需要构造一棵完整的二叉树,思路二则很简单,不需要构造二叉树,递归就完了。我这是为了挑战一下自己,因为好久没接触二叉树了,都忘得差不多了,呜呜呜~)
构造一个二叉树,使其能完整还原后序序列和中序序列所代表的二叉树,然后通过深度搜索的方式将先序二叉树输出。
#include<iostream>
using namespace std;
string a, b;
int r;
typedef struct ptree
{
char pnum; //二叉树节点
struct ptree* lf, * ri; //左右叶子节点
}pt, * ps;
//将后序序列的字母x对应到中序序列中,并返回x在中序序列中的位置
int finds(char x)
{
int j;
for (j = 0; j < r; j++)
{
if (a[j] == x)
break;
}
return j;
}
//查找根节点root的左子树中的根节点
int findless(int t, int root)
{
int i;
for (i = root; i >= 0; i--)
{
if (finds(b[i]) < t)
break;
}
return i;
}
//查找根节点root的右子树中的根节点
int findmore(int t, int root)
{
int i;
for (i = root; i >= 0; i--)
{
if (finds(b[i]) > t)
break;
}
return i;
}
//完整还原二叉树
//参数分别是二叉树、二叉树在中序序列中的起始位置、结束位置、
//以及根节点在后序序列中的位置
void gettree(ps* ctree, int begins, int ends, int root)
{
int len = ends - begins + 1;
if (len <= 0)
return;
(*ctree) = (ps)malloc(sizeof(pt));
(*ctree)->lf = NULL;
(*ctree)->ri = NULL;
(*ctree)->pnum = b[root];
int t = finds(b[root]); //找到根节点在中序序列中的位置
int j = findless(t, root); //找到根节点左子树的根节点
int s = findmore(t, root); //找到根节点右子树的根节点
//递归构造
if(j>=0)
gettree(&((*ctree)->lf), begins, t - 1, j);
if(s>=0)
gettree(&((*ctree)->ri), t + 1, ends, s);
}
//先序输出
void houxu(ps ctree)
{
if (ctree != NULL)
{
cout << ctree->pnum;
houxu(ctree->lf);
houxu(ctree->ri);
}
}
int main()
{
ps iwant;
cin >> a; cin >> b;
r = a.length();
gettree(&iwant, 0, r - 1, r - 1);
houxu(iwant);
return 0;
}
思路二
直接通过递归的方式将二叉树以先序序列的方式输出,简单直白,函数设计啥的和上面一样。
#include<iostream>
using namespace std;
string a, b;
int r;
int finds(char x)
{
int j;
for (j = 0; j < r; j++)
{
if (a[j] == x)
break;
}
return j;
}
int findless(int t,int root)
{
int i;
for (i = root; i >= 0; i--)
{
if (finds(b[i]) < t)
break;
}
return i;
}
int findmore(int t, int root)
{
int i;
for (i = root; i >= 0; i--)
{
if (finds(b[i]) > t)
break;
}
return i;
}
void gettree( int begins, int ends, int root)
{
int t = finds(b[root]);
int j = findless(t,root);
int s = findmore(t, root);
int len1 = t - begins;
int len2 = ends - t;
cout << b[root]; //直接按照先序的方式输出序列
if(len1>0 && j>=0)
gettree( begins, t-1, j);
if(len2>0 && s>=0)
gettree( t+1, ends, s);
}
int main()
{
cin >> a; cin >> b;
r = a.length();
gettree(0,r-1,r-1);
return 0;
}
后话
真的构造这二叉树花了好久,实在是对指针的指针难以理解,不过用构造二叉树的方式做完这题就感觉对指针的指针的理解清晰了很多。如果只是递归,这题确实简单,但是也可以挑战一下自己,尝试完整还原二叉树。