王道计算机机试——二叉树的还原

王道计算机机试——二叉树的还原

1.题目描述

给定一棵二叉树的前序遍历和中序遍历,求其后序遍历。(给定前序遍 历与中序遍历能够唯一确定后序遍历)
输入:
两个字符串,其长度 n 均小于等于 26。 第一行为前序遍历,第二行为中序遍历。二叉树中的结点名称以大写字母表 示:A,B,C…最多 26 个结点。
输出:
输入样例可能有多组,对于每组测试样例,输出一行,为后序遍历的字符串。

2.思路讲解

该例题涉及二叉树的建立、由二叉树的两种遍历结果还原二叉树、二叉树的 遍历等多种知识点。我们以分析该例题为例,介绍关于二叉树各知识点。 由该例要求,首先我们需要根据给定的二叉树前序和中序遍历结果还原该二 叉树。其次,我们需要将还原的二叉树以二叉树的形式保存在内存中。最后,我 们需要对建立的二叉树进行后序遍历。 后序遍历在前文中已经有所提及。下面我们对前两部分做重点的阐述。 由给定的前序和中序遍历还原得到该二叉树。以前序遍历结果 XDAGFE , 和中序遍历结果 ADGXFE 为例详细讨论其还原方法。
初始状态
由前序遍历结果的首个元素为 X 可知,原树必是由 X 为根结点。在中序遍 历中,遍历结果 ADGXFE 以 X 为界分为两个子串。其中第一个子串 ADG 为 X 的左子树的中序遍历结果,第二个子串 FE 为 X 的右子树的中序遍历结果。这样 我们知道 X 的左子树具有 3 个元素, X 的右子树具有 2 个元素。根据元素的数 量我们同样可以得知,在先序遍历中去除根结点 X 后剩余的串 DAGFE 中,前 3 个字符 DAG 为 X 的左子树的前序遍历结果,后 2 个字符 FE 为 X 的右子树的前 序遍历结果。
确定根节点
同样的对于确定的左子树前序遍历结果 DAG 和中序遍历结果 ADG 重复以 上确定过程,可知 D 为该子树根结点,其左儿子为 A,右儿子为 G。 X 的右子树前序遍历结果 FE 和中序遍历结果 FE 同样可以确定该子树以 F 为根节点,其左儿子不存在,右儿子为 E。
还原结果
这样我们就还原了原始二叉树。 我们还需将还原出来的树保存在内存中。使用结构体:

struct Node {//树节点结构体
	Node* lchild;
	Node* rchild;
	char c;
}

表示树的一个结点,其字符信息保存在字符变量 c,若该结点存在左儿子或 者右儿子,则指向他们的指针保存在 lchild 或 rchild 中,否则该指针为空。

3.代码

#include<iostream>
#include<string>
using namespace std;
struct Node {//树节点结构体
	Node* lchild;
	Node* rchild;
	char c;
}Tree[50]; //静态内存分配数组
int loc;//静态数组中已经分配的节点个数
Node* create() {//申请一个节点空间,返回指向其的指针
	Tree[loc].lchild = Tree[loc].rchild = NULL;//初始化左右儿子为空
	return &Tree[loc++];//返回指针,且loc累加
}
char str1[30], str2[30];//保存前序和中序遍历结果字符串
void postOrder(Node* T) {//后序遍历
	if (T->lchild != NULL) {//若左子树不为空
		postOrder(T->lchild);//递归遍历其左子树
	}
	if (T->rchild != NULL) {//若右自子树不为空
		postOrder(T->rchild);//递归遍历其右子树
	}
	cout << T->c;//遍历该节点,输出其字符信息
}
Node* build(int s1, int e1, int s2, int e2) {//由字符串的前序遍历和中序遍历还原树,
	//并返回其根节点,其中前序遍历结果为str1[s1]-str1[e1],中序遍历结果为str2[s2]-str2[e2]
	Node* ret = create();//为该树根节点申请空间
	ret->c = str1[s1];//该节点为前序遍历中的第一个字符
	int rootldx;
	for (int i = s2; i <= e2; i++) {//查找该根节点在中序遍历中的位置
		if (str2[i] == str1[s1]) {
			rootldx = i;
			break;
		}
	}
	if (rootldx != s2) {//若左子树不空
		ret->lchild = build(s1 + 1, s1 + (rootldx - s2), s2, rootldx - 1);//递归还原其左子树
	}
	if (rootldx != e2) {//若右子树不空
		ret->rchild = build(s1 + (rootldx - s2) + 1, e1, rootldx + 1, e2);//递归还原其右子树
	}
	return ret;//返回根节点指针
}
int main() {
	while (cin >> str1 >> str2) {
		loc = 0;//初始化静态内存空间中已经使用的节点数为0
		int L1 = strlen(str1);
		int L2 = strlen(str2);//计算两个字符串长度
		Node* T = build(0, L1 - 1, 0, L2 - 1);//还原整棵树,其根节点指针保存在T中
		postOrder(T);//后序遍历
		cout << endl;
	}
	return 0;
}

4.小结——静态数组与动态

在本例代码中我们并没有动态的申请内存空间,并在程序结束时释放这些空间。而是使用了静态数组,利用分配数组元素给相应的结点实现内存分配。这是 对内存分配较为简单的实现方法,若读者对动态的申请和释放内存没有把握,或者对何时何地释放内存抱有疑惑,建议使用该较为保险的方法。

发布了12 篇原创文章 · 获赞 12 · 访问量 171

猜你喜欢

转载自blog.csdn.net/xyzxyzxyz1999/article/details/104749444
今日推荐