原理比较简单,首先找到前序的第一个元素,寻找在中序中间的位置,这样中序输出被分成两部分,那么这前一部分属于左子树,后面部分属于右子树,如下所示:
a b d c e f
d b a e c f
分解成为
b d
d b
和
c e f
e c f
然后对这两个部分采用类似的方法求解,最后得到二叉树的描述,代码如下:
/*
* bop_3_9.cpp
*
* Created on: 2012-5-25
* Author: ict
*/
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <string>
#include <iostream>
using namespace std;
#define MAX 100
typedef struct NODE
{
char ch;
struct NODE *left;
struct NODE *right;
}Node, *PNode;
//递归调用该方法,根据前序、中序得到后序
PNode pre_in_to_af(char *pre, char *in)
{
int index;
char a1[MAX]; //用于分割字符串
char a2[MAX];
char b1[MAX];
char b2[MAX];
memset(a1, 0, MAX * sizeof(char));
memset(a2, 0, MAX * sizeof(char));
memset(b1, 0, MAX * sizeof(char));
memset(b2, 0, MAX * sizeof(char));
if(strlen(pre) == 0)
return NULL;
if(strlen(in) == 0)
return NULL;
string a = pre;
string b = in;
PNode p = (PNode)malloc(sizeof(Node));
index = b.find_first_of(pre[0], 0); //找到指定字符在中序的位置
p->ch = pre[0];
strncpy(a1, pre + 1, index); //复制前序的前半部分
strncpy(a2, pre + index + 1, strlen(pre) - index -1); //复制前序的后半部分
strncpy(b1, in, index); //复制中序的前半部分
strncpy(b2, in + index + 1, strlen(in) - index -1); //复制中序的后半部分
p->left = pre_in_to_af(a1, b1); //递归查找左子树
p->right = pre_in_to_af(a2, b2); //递归查找右子树
return p; //返回根节点
}
//后序遍历
void afPrint(PNode p)
{
if(p == NULL)
return ;
afPrint(p->left);
afPrint(p->right);
printf("%c", p->ch);
return ;
}
int main()
{
char pre[MAX];
char in[MAX];
PNode head;
scanf("%s", pre);
scanf("%s", in);
head = pre_in_to_af(pre, in);
afPrint(head);
return 0;
}
------------------------------------------------华丽的分割线---------------------------------------------------
编程之美后面的扩展问题,如果知道前序和后序遍历的结果,能重构二叉树吗?
这里我们可以做一个一般的结论:
知道两种遍历结果,如果要重构二叉树,其中一种必须是中序遍历结果。
为什么会这样呢?我们首先可以举一个非常简单的例子:
前序遍历:a b
后序遍历:b a
我们可以构造两棵二叉树满足上面的遍历结果,两棵二叉树的根节点都是a,但是一棵二叉树的b为左节点,一棵二叉树的b为右节点。
为什么会这样呢?
原来在前序遍历结果中,第一个遍历元素一定是二叉树的根节点;后续遍历结果中,最后一个元素一定是二叉树的根节点;这样的话,我们只能确定根节点,但是我们不知道中间元素哪些是左子树,哪些属于右子树,从而无法构造独一无二的二叉树。
相反,如果知道中序遍历,我们通过前序或者后序获得根节点,然后在中序中找到根节点,那么根节点左半部分为左子树,右半部分为右子树,从而可以唯一确定二叉树。
所以类似的,我们可以根据后序遍历+中序遍历,确定二叉树,代码和上面类似。