SWUST OJ# 利用中序和前序遍历求后序遍历

目录

题目

思路

代码

暴力建树

递归深搜


题目

题目描述

已知二叉树的中序和先序遍历可以唯一确定后序遍历、已知中序和后序遍历可以唯一确定先序遍历,但已知先序和后序,却不一定能唯一确定中序遍历。现要求根据输入的中序遍历结果及先序遍历结果,要求输出其后序遍历结果。

输入

输入数据占2行,其中第一行表示中序遍历结果,第二行为先序遍历结果。

输出

对测试数据,输出后序遍历结果。

样例输入

BFDAEGC
ABDFCEG

样例输出

FDBGECA

思路

首先先要把三种遍历弄明白

前序遍历:★根->左孩子->右孩子

中序遍历:左孩子->★根->右孩子

后序遍历:左孩子->右孩子->★根

关键突破点就是遍历时根节点的位置

根据定义,前序遍历的第一个节点即是根节点

找到根节点,再确定根节点中序序列中的位置,就可以分出左右两棵子树。

然后再在已知的左右子树中,故技重施,再次按照上述步骤,再次

找到左右孩子的根节点,再确定左右孩子的根节点中序序列中的位置,就可以再次分出左右两棵子树。

下面假设某棵树的前序遍历:ABCDEFGH。中序遍历:CBEDFAHG。

分界线标准:前序:左右子树以 | 分开。左边是左子树,右边是右子树。

中序:已经判断根节点的左右两边以 | 分开。左边是左子树,右边是右子树。

[ ]表示当前搜索的区间

红色表示当前步骤的判断,蓝色表示已经搜过的节点。

(注意想明白分界线 | 和 [] ,这对之后代码的理解有极大帮助)

第①步:

前序:[BCDEF | GH]  中序:[CBEDF | A | HG]

可知:第一层就是A,也就是根节点,同时可以得知 A的左孩子(左子树)的元素有:BCDEF,右孩子(右子树)的元素有:GH 。

第②步:

前序:A  [C | DEF][G H]   中序:[C | B | EDF]A[H | G]

可知:第二层是B和G。也就是左右孩子的根节点,同时可以得知 B的左孩子(左子树)的元素只有C,右孩子(右子树)的元素有:DEF。G的左孩子(左子树)只有H,且不存在右孩子。

第③步:

前序:AB  [C][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;
}

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

青衿白首志

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值