目录
题目
题目描述
已知二叉树的中序和先序遍历可以唯一确定后序遍历、已知中序和后序遍历可以唯一确定先序遍历,但已知先序和后序,却不一定能唯一确定中序遍历。现要求根据输入的中序遍历结果及先序遍历结果,要求输出其后序遍历结果。
输入
输入数据占2行,其中第一行表示中序遍历结果,第二行为先序遍历结果。
输出
对测试数据,输出后序遍历结果。
样例输入
BFDAEGC ABDFCEG样例输出
FDBGECA
思路
首先先要把三种遍历弄明白
前序遍历:★根->左孩子->右孩子
中序遍历:左孩子->★根->右孩子
后序遍历:左孩子->右孩子->★根
关键突破点就是遍历时根节点的位置
根据定义,前序遍历的第一个节点即是根节点。
找到根节点,再确定根节点在中序序列中的位置,就可以分出左右两棵子树。
然后再在已知的左右子树中,故技重施,再次按照上述步骤,再次
找到左右孩子的根节点,再确定左右孩子的根节点在中序序列中的位置,就可以再次分出左右两棵子树。
下面假设某棵树的前序遍历:ABCDEFGH。中序遍历:CBEDFAHG。
分界线标准:前序:左右子树以 | 分开。左边是左子树,右边是右子树。
中序:已经判断根节点的左右两边以 | 分开。左边是左子树,右边是右子树。
[ ]表示当前搜索的区间
红色表示当前步骤的判断,蓝色表示已经搜过的节点。
(注意想明白分界线 | 和 [] ,这对之后代码的理解有极大帮助)
第①步:
前序:[A BCDEF | GH] 中序:[CBEDF | A | HG]
可知:第一层就是A,也就是根节点,同时可以得知 A的左孩子(左子树)的元素有:BCDEF,右孩子(右子树)的元素有:GH 。
第②步:
前序:A [B C | DEF][G H] 中序:[C | B | EDF]A[H | G]
可知:第二层是B和G。也就是左右孩子的根节点,同时可以得知 B的左孩子(左子树)的元素只有C,右孩子(右子树)的元素有:DEF。G的左孩子(左子树)只有H,且不存在右孩子。
第③步:
前序:AB [C][D E | F] G [H] 中序:[C]B[E | D | F]A[H]G
可知:第三层是C,D和H。并且C和H是叶子节点,同时可以得知 D的左孩子是E,右孩子是F。
第④步:
前序:ABCD [E][F] GH 中序:CB[E]D[F]AHG
可知:第四层是E和F,并且他们都是叶子节点。
所以根据以上分析,按照每一步的步骤,已经可以画出这棵树了
A / \ B G / \ / C D H / \ E F
然而说了这么多,题目是要求我们求出后续遍历呀,但我们只是有能力求出这棵树。
所以:方法①就是建一颗树。依次按照上述步骤,找到根节点,然后递归在左子树和右子树做相同的操作。最后按照后序遍历输出。
//建树
string pro,ord;
//sp,ep表示搜索pro,也就是搜索前序遍历每一个元素
//sp表示从前往后搜,ep表示从后往前搜
//so,eo表示搜索ord,也就是搜索中序遍历每一个元素
//so表示从前往后搜,eo表示从后往前搜
void BTCreate(BinaryTree* &root,int sp,int ep,int so,int eo) {
if(sp>ep||so>eo) { //当前指标大于后指标,说明不存在孩子节点了,置空返回
root=NULL;
return;
}
int i,j;
for(i=sp,j=so;i<=ep;i++,j++) {
if(ord[j]==pro[sp]) break;//找到相同的了,那么他就是根节点
}
BTNode *q;
q=new BTNode;
q->data=pro[sp];//创建新的节点
root=q;//连到二叉树上
BTCreate(q->left,sp+1,i,so,j-1);//递归创建左孩子
BTCreate(q->right,i+1,ep,j+1,eo);//递归创建右孩子
//为什么这样搜索子区间?想想之前解析过程的分界线。
}
但是,这样毕竟还是太费时间了,还要花内存去建树。
于是就有了方法②:在建树的同时就后序遍历输出,不用再真正的建树。相当于是一个虚拟的树结构。
//建树
string pro,ord;
//sp,ep表示搜索pro,也就是搜索前序遍历每一个元素
//sp表示从前往后搜,ep表示从后往前搜
//so,eo表示搜索ord,也就是搜索中序遍历每一个元素
//so表示从前往后搜,eo表示从后往前搜
void dfs(int sp,int ep,int so,int eo) {
if(sp>ep||so>eo) return;
for(int i=sp,j=so;i<=ep;i++,j++) {
if(ord[j]==pro[sp]) { //找到相同的了,那么他就是根节点
dfs(sp+1,i,so,j-1);//搜索左孩子
dfs(i+1,ep,j+1,eo);//搜素右孩子
//为什么这样搜索子区间?想想之前解析过程的分界线。
cout<<ord[j];
}
}
}
代码
暴力建树
#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cmath>
using namespace std;
//定义二叉树
typedef struct BTNode {
char data;
BTNode *left;
BTNode *right;
}BinaryTree;
//建树
string pro,ord;
void BTCreate(BinaryTree* &root,int sp,int ep,int so,int eo) {
if(sp>ep||so>eo) { //当前指标大于后指标,说明不存在孩子节点了,置空返回
root=NULL;
return;
}
int i,j;
for(i=sp,j=so;i<=ep;++i,++j) {
if(ord[j]==pro[sp]) break;//找到相同的了,那么他就是根节点
}
BTNode *q;
q=new BTNode;
q->data=pro[sp];
root=q;
BTCreate(q->left,sp+1,i,so,j-1);//递归创建左孩子
BTCreate(q->right,i+1,ep,j+1,eo);//递归创建右孩子
}
//后序遍历
void BTPostOrder(BinaryTree* &root) {
if(root==NULL) return;
BTPostOrder(root->left);
BTPostOrder(root->right);
cout<<root->data;
}
int main() {
cin>>ord>>pro;
BinaryTree *root;
BTCreate(root,0,pro.size()-1,0,ord.size()-1);
BTPostOrder(root);
return 0;
}
递归深搜
#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cmath>
using namespace std;
string pro,ord;
void dfs(int sp,int ep,int so,int eo) {
if(sp>ep||so>eo) return;
for(int i=sp,j=so;i<=ep;i++,j++) {
if(ord[j]==pro[sp]) { //找到相同的了,那么他就是根节点
dfs(sp+1,i,so,j-1);//搜索左孩子
dfs(i+1,ep,j+1,eo);//搜素右孩子
cout<<ord[j];
}
}
}
int main() {
cin>>ord>>pro;
dfs(0,pro.size()-1,0,ord.size()-1);
return 0;
}