正常来说只是知道一个二叉树的先序遍历是无法确定一颗二叉树的,但是作为一棵满二叉树,树的形态确定了,先序遍历的顺序固定为“根左右”的条件下,二叉树也就得以确定。
那么在满二叉树的情况下,如何通过先序遍历得到后续遍历呢?让我们先进行下面的分析
通常情况下,我们进先序遍历,一定是从根出发,于是这个根便是整个序列中的“头部”:
a | bdecfg
去掉“头部”以后,剩下的部分由左右两个子树构成。因为先序是根左右的次序,观察&思索也可以发现——剩下的部分从中间等分刚好可以得到这颗树的左右两个子树部分:
bde | cfg
化分出来的两个子序列也是子树的先序遍历:
b | de c | fg
于是就形成一个“套娃”。然后首先会做出的反应就是递归。
既然已经确定使用递归了,现在的关键就是:怎么递归、在哪里使用递归、递归做什么以及递归结束的条件(也就是寻找最小的子问题)。
通过上述实例的分析,整个满二叉树先序遍历的序列可以的流程为:根->左子树->右子树;每颗子树的流程也是:根->左子树->右子树。现在要获取的是后续遍历,在不耗费额外空间时间重构整棵树的条件下,把这个过程改为:左子树->右子树->根。
在先序序列中如何体现 左子树->右子树->根 的流程呢?不妨在草稿纸上用刚刚的实例看看:
后序(左右根):((de) b)->((fg)c)-> a
可以发现实际上就是把序列分成:头部、左部、右部,然后按照:左部——右部——头部 的顺序输出。头部只有一个字符,左右部本身视作一个新的先序序列再次划分三部。
划到后面,左右部只剩下三个字符组成的先序,此时最后一次化分,可以得到只有头部的先序序列,只需要输出头部就可以了,然后逐步回代,因为子树的头输出过了,组成大树以后,其左右子树也就已经完成的输出,也只需要输出头部即可。
于是就有:
化分函数(树先序序列 ){
if(序列数量大于一个){
化分函数(左子树先序序列);
化分函数(右子树先序序列);
}
输出根(头部序列);
}
#include <iostream>
//#include<cmath>
using namespace std;
#define GET_ARRAY_LEN(array, len) { len = sizeof(array)/sizeof(array[0]);}
//获取数组长度宏
/**
* @a 目标数组
* @begin 子树开始的下标
* @end 子树结束的下标
**/
void preToPost(char a[],
int begin,
int end
) {
char root = a[begin]; //暂存根,子树遍历完后最后输出
if (begin + 1 <= end) {
begin = begin + 1;//左端+1(把根节点独立出来)
//此时左子树从begin+1位开始,到(begin+end)/2)结束
preToPost(a, begin,((begin+end)/2)); //左子树
//此时右子树从((begin+end)/2) + 1)位开始,到end结束
preToPost(a, (((begin+end)/2) + 1), end); //右子树
}
cout << root; //输出根
}
void main() {
//测试数据
//char a[] = { 'a','b','d','h','i','e','j','k','c','f','l','m','g','n','o' };
char a[] = { 'a','b','d','e','c','f','g' };
int length; //字符数组长度
/*
这里可以添加一个判断序列数量是否满足满二叉树的判定
if ( ) {
cout<<"输出数据不满足满二叉树条件"<<endl;
return ;// 判断是否满足满二叉树的条件
}*/
GET_ARRAY_LEN(a, length); //调用写好的宏得到数组长度
//测试数据输出
cout<<"先序遍历为:"<<endl;
for (int i = 0;i < length;i++)
cout << a[i];
cout << endl;
cout<<"后序遍历为:"<<endl;
preToPost(a, 0, length - 1);
cout << endl << endl << endl << endl;
}