二叉樹的前序、中序、後序遍歷的定義: 前序遍歷:對任一子樹,先訪問跟,然後遍歷其左子樹,最後遍歷其右子樹; 中序遍歷:對任一子樹,先遍歷其左子樹,然後訪問根,最後遍歷其右子樹; 後序遍歷:對任一子樹,先遍歷其左子樹,然後遍歷其右子樹,最後訪問根。 給定一棵二叉樹的前序遍歷和中序遍歷,求其後序遍歷(提示:給定前序遍歷與中序遍歷能夠唯一確定後序遍歷)。
輸入描述:
兩個字符串,其長度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);
}