【题目描述】
输入一棵二叉树的先序和中序遍历序列,输出其后序遍历序列。
【输入】
共两行,第一行一个字符串,表示树的先序遍历,第二行一个字符串,表示树的中序遍历。树的结点一律用小写字母表示。
【输出】
一行,表示树的后序遍历序列。
【输入样例】
abdec
dbeac
【输出样例】
debca
这是一道已知先序和中序,求后序的程序。我将用递归的方法来讲解这一道题。刷到现在,发现做题是一件及其有意思的一件事。我希望把这种快乐传递给更多的人,哈哈哈哈哈。
1.了解先序、中序和后序遍历的访问顺序;
先序遍历:先访问根结点,再遍历左子树,最后遍历右子树,意味着在结点的先序序列中,第一个结点必定是根,假设为root(对于每颗子树也成立);
中序遍历:先遍历左子树,再访问根结点,最后遍历右子树,所有结点root正好把中序序列分成了两部分,root之前的应该是左子树上的结点,root之后的应该是右子树上的结点;
后序遍历:先遍历左子树,再遍历右子树,最后访问根结点,意味着在结点的后序遍历中,最后一个结点必定是根结点(对于每颗子树也成立)。
既然这样,我们只要递归将一棵大树分成两颗子树,然后找他们的父结点,不断递归输出;
2.根据中序遍历的访问顺序可以得知,它的根结点、左子树和右子树是非常好找的,比如,中序遍历的起点是l2,终点是r2;
定义pos为根结点的下标
那么,中序遍历的左子树起止范围:(l2, pos-1) ,右子树起止范围:(pos+1, r2)
就是根结点pos将其分为左右两边,左边就是左子树,右边就是右子树。
3.先序遍历的左子树和右子树不好找,难点在于找到先序遍历中的左子树和右子树。
睁大眼睛看好啦,哈哈哈哈
根据中序遍历的定义,得知
左子树长度:pos-L2;
右子树长度:R2-pos;
规定先序遍历的起点是L1,终点是R1;
先序遍历左子树的边界分析如下:
起点:L1+1(起点+1,因为起点是根结点,所以从下一个结点开始);
终点有两种计算方法:第一种:(起点+左子树长度-1) L1+1+(pos-L2)-1 = L1+pos-L2;
终点有两种计算方法:第二种:(终点-右子树长度) R1-(R2-pos);
先序遍历右子树的边界分析如下:
因为左子树的终点有两种方法,那么右子树就有两种写法。
第一种:
起点:(第一种方法起点+1)L1+pos-L2+1;
终点:(起点+右子树长度-1)L1+pos-L2+1+(R2-pos)-1 = L1-L2+R2;
第二种:
起点:(第二种方法起点+1)R1-(R2-pos)+1;
终点:(起点+右子树长度-1)R1-(R2-pos)+1+(R2-pos)-1 = R1;
确定好左右子树的边界以后,递归就很好写了,所以递归就有两种写法:
第一种:
if(pos > L2) dg(L1+1, L1+pos-L2, L2, pos-1);//递归左子树
if(pos < R2) dg(L1+pos-L2+1, L1-L2+R2, pos+1, R2);//递归右子树
第二种:
if(pos > L2) dg(L1+1, R1-(R2-pos), L2, pos-1);//递归左子树
if(pos < R2) dg(R1-(R2-pos)+1, R1, pos+1, R2);//递归右子树
#include<bits/stdc++.h>
using namespace std;
string before, between;
//左子树长度:pos-l2
//右子树长度:r2-pos
void dg(int l1, int r1, int l2, int r2){
int pos = between.find(before[l1]);
//方法一:
// if(pos > l2) dg(l1+1, l1+pos-l2, l2, pos-1);//递归左子树
// if(pos < r2) dg(l1+pos-l2+1, l1-l2+r2, pos+1, r2);//递归右子树
//方法二:
if(pos > l2) dg(l1+1, r1-(r2-pos), l2, pos-1);//递归左子树
if(pos < r2) dg(r1-(r2-pos)+1, r1, pos+1, r2);//递归右子树
cout << between[pos];
}
int main(){
cin >> before >> between;
dg(0, before.size()-1, 0, between.size()-1);
return 0;
}
小仙女~小帅哥看过来,是不是对求后序排列有了全面的了解,那就点个赞再走吧。