二叉樹遍歷 九度教程第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);
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章