首先我们都知道,通过一段中序序列和前序序列能够确定一个二叉树,同理中序和后序也能确定(注意必须包括中序)
这边顺便复习一下三者区别:
先序:根,左子树,右子树
中序:左子树,根,右子树
后序:左子树,右子树,根
现在我们需要尝试一下它的实际代码操作是怎样的:
首先这是一个由中序遍历和前序遍历输出后序遍历的代码
#include <iostream>
#include <cmath>
#include <string>
using namespace std;
string pre, in; //pre表示前序,in表示中序
void work(int al,int ar,int bl,int br) {//分别对应前序的最左边下标,最右边下标,中序的最左边下标,最右边下标
if (al > ar || bl > br) return; //判断递归结束条件
char root = pre[al]; //前序的首位即是该段树的根
int k = in.find(root); //找到根在中序中的下标
work(al + 1, al + k - bl, bl, k - 1); //递归左子树
work(al + k - bl + 1, ar, k + 1, br); //递归右子树
cout << root;
}
int main()
{
cin >> in >> pre; //先输入中序再输入前序
int len = pre.length() - 1; //注意我们这里下标从0开始,所以长度-1
work(0, len, 0, len);
return 0;
}
先看主函数,分别输入中序和前序的字符串,随后获取长度,然后调用函数work
接下来重点分析work函数的参数,al表示前序左边界的下标,ar表示前序右边界下标,同理bl,br就是中序下标
work函数的实现原理在注释中写的很清楚,这里简单说一下:
例如我们输入的中序和前序分别是:
ABEDFCHG CBADEFGH
(下标从0开始)
那么进入函数时我们需要根据前序获得树的根C,然后在中序中找到C对应的下标,得到k=5
然后我们根据这个k值就可以在两个序列中分别截断左子树和右子树,然后将下标递归work函数就可以,最后再输出此时的树根,可以获得后序序列:
AEFDBHGC
这个输出放在两个递归后面,形式其实非常像二叉树的后序输出,一个道理
当然如果你不理解这个work函数里面参数传递的值,这里会特别讲一下:
假如对于上面的输入,我们可以知道第一次进入函数得到root='C',k=5
k=5意味着中序中'C'的左边有5个元素而右边有2个元素
那么接下来我们要递归根为C的左子树和右子树,自然要删除C
那么此时左子树的al=al+1=1(删去第一个元素),ar=al+k-bl=5(注意-bl想要理解最好自己测试一下对于右子树的递归) bl=bl=0,br=k-1=4(也就是k的左边)
对于右子树al=al+k-bl+1=6(左子树前序的右下标+1 ),ar=ar=7 bl=k+1=6,br=br=7
非常简单,建议自己对于这组输入自己画一下提高理解
最后这组数据来自于洛谷的题,大家可以自己写一写试试
[USACO3.4] 美国血统 American Heritage - 洛谷
当然上面那个只是中序和前序获得后序,接下来同理给出后序和中序得到前序的函数代码
#include <iostream>
#include <cmath>
#include <string>
using namespace std;
string info, post;
void work(int al,int ar,int bl,int br) {
if (al > ar || bl > br) return;
char root = post[ar];
int k = info.find(root);
cout << root;
work(al, al + k - 1 - bl, bl, k - 1);
work(al + k - bl, ar - 1, k + 1, br);
}
int main()
{
cin >> info >> post;
int len = post.length() - 1; //因为下标从0开始,所以-1
work(0, len, 0, len);
cout << endl;
return 0;
}
这里是先输入中序in,再输入后序post,这里和前面不同点要注意:
root根是后序post的下标ar的位置,也就是post的尾部
然后输出根要放在递归前面,因为先序是先输出根嘛
最后这个函数的参数也有所改变,自己理解一下即可
来自于: