二叉树遍历 九度教程第32题 由二叉树的前序遍历与中序遍历确定该二叉树

添加链接描述

二叉树的前序、中序、后序遍历的定义: 前序遍历:对任一子树,先访问跟,然后遍历其左子树,最后遍历其右子树; 中序遍历:对任一子树,先遍历其左子树,然后访问根,最后遍历其右子树; 后序遍历:对任一子树,先遍历其左子树,然后遍历其右子树,最后访问根。 给定一棵二叉树的前序遍历和中序遍历,求其后序遍历(提示:给定前序遍历与中序遍历能够唯一确定后序遍历)。
输入描述:
两个字符串,其长度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);
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章