关于二叉树重建的问题

首先了解什么是二叉树重建:

我们知道, 二叉树的遍历有三种, 前序, 中序, 后序.

我们可以根据其中的任意两种遍历得到的序列, 求出这颗二叉树另一种遍历序列~


如给出前序与中序

前: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;
}


看完以上两个示例, 不知道你们有没有找出点规律的蛛丝马迹, 看不懂的话仔细的揣摩两遍, 就懂了

这里有些很有规律的东西, 记下后可以当初一个伪模板

求后序时, 递归函数的顺序是, 递归左子树 -> 递归右子树 -> 保存根 (和后序的结构一样, 是左-右-根)

求前序时, 递归函数的顺序是, 保存根 -> 递归左子树 -> 递归右子树 (同理, 和前序的结构一样)


不难, 最后根据前后序求中序的代码, 读者自己敲吧~

 

 

posted @ 2013-08-19 20:47  pangbangb  阅读(205)  评论(0编辑  收藏  举报