关于二叉树重建的问题
首先了解什么是二叉树重建:
我们知道, 二叉树的遍历有三种, 前序, 中序, 后序.
我们可以根据其中的任意两种遍历得到的序列, 求出这颗二叉树另一种遍历序列~
如给出前序与中序
前:4231756
中:3214576
求其后序遍历(3125674)
思路: 前序遍历中的第一个字符, 毫无疑问肯定是根结点! (在上面给出的前序中, 第一个字符是4)
根据而在中序中, 该根结点是一个分水岭, 它将中序序列变成了两半, 左一半是根结点的左儿子部分(321), 右一半是右儿子部分(576)
由于要求的是后序序列, 我们得到的根节点, 事实上是要放在最后的, 我们可以用一个ans数组保存(稍后体会)
接着, 我们要对第一个根结点4的左儿子部分进行剖析, 把左儿子部分也建成树, 同理, 稍后把右儿子部分建成树~(递归!)
理解了上面的思路后, 看看代码实现的过程
#include<stdio.h> #include<string.h> //n表示待查序列长度, s1是前序序列, s2是中序序列, s是ans数组的指针 void build(int n, char *s1, char *s2, char *s) { if(n <= 0) return; int p = strchr(s2, s1[0]) - s2; //根据前序, 在中序中找到根节点(可用上面给出的数据模拟, 假设第一个根节点是4) build(p, s1+1, s2, s); //左儿子部分, 试着理解: 前序s1的第一个4找到后, 接下来的s1+1是左儿子部分的根结点(即2), p是左儿子的长度 build(n-1-p, s1+p+1, s2+p+1, s+p); //右儿子部分, 长度为n-p-1, 是总长度减左树再减根结点1个(即4), s1+p+1 and s2+p+1 同理 s[n-1] = s1[0]; //由于求后序遍历, 所以最后将开头得到的根结点, 放在答案数组的最后~ } int main() { char str1[30], str2[30]; char ans[30]; int len; while(scanf("%s%s", str1, str2) != EOF) { len = strlen(str1); build(len, str1, str2, ans); ans[len] = '\0'; puts(ans); } return 0; }
再看看由中序和后序求前序:
数据还是原先的数据:
中:3214576
后:3125674
求其前序遍历(4231756)
思路转化一下, 可以先由后序的最后一个字符可知根结点.
注意点:
不同与前面由前序中序的重建, 所求后序的保存顺序是 左子树 -> 右子树 -> 根
因此, 在上面求后序的时候, 是在把左子树和右子树都递归好了后, 再最后把根放在答案数组的最后面
而在根据中序和后续求前序的过程中, 我们前序的保存顺序是 根 -> 左子树 -> 右子数
所以, 我们一开始求得根结点后就要保存下来, 再去递归左右子树!
看代码理解:
#include<stdio.h> #include<string.h> int pos; void build(int n, char *s1, char *s2, char *s) { if(n <= 0) return ; int p = strchr(s1, s2[n-1]) - s1; //找根 s[0] = s2[n-1]; //先保存根节点! 还是放在答案数组的开头! 体会和上面保存方式的不一样! build(p, s1, s2, s+1); //左子树, 与上面同理, 但是在中序结构(左-根-右)和后序结构(左-右-根)中, 左树都是在开头, 于是s1 s2都从开头继续递归 build(n-p-1, s1+p+1, s2+p, s+p+1); //右子树, 根据中序结构, 在找到根后, s1要减去左树长度(p), 再减去根长度(1), 而后序s2只要减去左树长度 } int main() { char str1[50]; char str2[50]; while(scanf("%s%s", str1, str2) != EOF) { char ch, ans[50]; int len = strlen(str1); pos = 0; build(len, str1, str2, ans); ans[len] = '\0'; puts(ans); } return 0; }
看完以上两个示例, 不知道你们有没有找出点规律的蛛丝马迹, 看不懂的话仔细的揣摩两遍, 就懂了
这里有些很有规律的东西, 记下后可以当初一个伪模板
求后序时, 递归函数的顺序是, 递归左子树 -> 递归右子树 -> 保存根 (和后序的结构一样, 是左-右-根)
求前序时, 递归函数的顺序是, 保存根 -> 递归左子树 -> 递归右子树 (同理, 和前序的结构一样)
不难, 最后根据前后序求中序的代码, 读者自己敲吧~