二叉树的前序、中序、后序遍历的定义: 前序遍历:对任一子树,先访问跟,然后遍历其左子树,最后遍历其右子树; 中序遍历:对任一子树,先遍历其左子树,然后访问根,最后遍历其右子树; 后序遍历:对任一子树,先遍历其左子树,然后遍历其右子树,最后访问根。 给定一棵二叉树的前序遍历和中序遍历,求其后序遍历(提示:给定前序遍历与中序遍历能够唯一确定后序遍历)。
输入描述:
两个字符串,其长度n均小于等于26。
第一行为前序遍历,第二行为中序遍历。
二叉树中的结点名称以大写字母表示:A,B,C…最多26个结点。
输出描述:
输入样例可能有多组,对于每组测试样例,
输出一行,为后序遍历的字符串。
示例1
输入
ABC
BAC
FDXEAG
XDEFAG
输出
BCA
XEDGAF
题目分析:
该例题涉及二叉树的建立、由二叉树的两种遍历结果还原二叉树、二叉树的遍历等多种知识点。
解题思路:
首先我们需要根据给定的二叉树前序和中序遍历结果还原该二叉树。其次,我们需要将还原的二叉树以二叉树的形式保存在内存中。最后,对建立的二叉树进行后序遍历。
AC代码:
#include<iostream>
#include<stdio.h>
#include<string.h>
using namespace std;
struct node {//树结点结构体
node *left;//左孩子结点指针
node *right;//右孩子结点指针
char c;//结点字符信息
}tree[50];//静态内存分配数组
int cnt;//静态数组中已经分配的结点个数
node *creat() {//申请一个结点空间,返回指向其的指针
tree[cnt].left = tree[cnt].right = NULL;//初始化左右儿子为空
return &tree[cnt++];//返回指针,且计数器累加
}
char str1[30]; char str2[30];//保存前序和中序遍历结果字符串
void post_order(node *t) {//后序遍历
if (t == NULL) return;
if (t->left != NULL) {//首先递归遍历其左子树
post_order(t->left);
}
if (t->right != NULL) {//递归遍历其右子树
post_order(t->right);
}
printf("%c", t->c);//遍历该结点
}
node *build(int s1, int e1, int s2, int e2) {//由字符串的前序遍历和中序遍历还原树, 并返回其根节点, 其中前序遍历结果为由str1[s1] 到str1[e1] ,中序遍历结果为str2[s2]到str2[e2]
node *ret = creat();//为树的根结点申请空间
ret->c = str1[s1];//根结点的字符为前序遍历中的第一个字符
int rootidx;
for (int i = s2; i <= e2; i++) {//查找该根结点字符在中序遍历中的位置
if (str2[i] == str1[s1]) {
rootidx = i;
break;
}
}
if (rootidx != s2) {//若左子树不为空
ret->left = build(s1 + 1, s1 + (rootidx - s2), s2, rootidx - 1);//递归还原左子树
}
if (rootidx != e2) {//若右子树不为空
ret->right = build(s1 + (rootidx - s2) + 1, e1, rootidx + 1, e2);//递归还原右子树
}
return ret;
}
int main() {
while (scanf("%s%s", str1, str2)!=EOF) {
cnt = 0;//将静态内存空间中已经使用的结点个数初始化为0
int len1 = strlen(str1);
int len2 = strlen(str2);
node *t = build(0, len1 - 1, 0, len2 - 1);
post_order(t);//后序遍历
printf("\n");
}
return 0;
}
在这里我们使用了静态数组,利用分配数组元素给相应的结点实现内存分配,即在申请结点时使用了“动态化静态”的思想。
在算法竞赛入门经典这本书中,作者指出了这种申请结点的坏处在于“释放内存”很不方便,如果反复执行新建结点和删除结点,cnt则会一直增加,但是已经用完的内存却无法重用。在大多数的算法竞赛题目中,这并不会引起问题。这个问题的常见解决方案是写一个简单的内存池,具体来说就是维护一个空闲列表,初始时把上述tree数组中的所有元素的指针放到该列表中,如下所示:
可以用静态数组配合空闲列表来实现一个简单的内存池。
#define maxn 100
queue<node *> freenodes;
node tree[maxn];
void init() {
for (int i = 0; i < maxn; i++) {
freenodes.push(&tree[i]);//初始化内存池
}
}
node *newnode() {
node *u = freenodes.front();
freenodes.pop();
u->left = u->right = NULL;
return u;
}
void deletenode(node *u) {
freenodes.push(u);
}