zoukankan      html  css  js  c++  java
  • 关于二叉树重建的问题

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

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

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


    如给出前序与中序

    前: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] = '';
            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] = '';
            puts(ans);
        }
        return 0;
    }
    


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

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

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

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


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

  • 相关阅读:
    51nod 1163 最高的奖励
    51nod 1191 消灭兔子
    51nod 2538 二三排列
    node做渲染服务器的实现
    gulp静态服务器的搭建
    canvas中裁切(橡皮檫)的应用--探照灯,点击去除遮罩
    canvas中图片、视频的加载(截图、切片)
    canvas中笔触基础知识
    Gitee的使用流程
    gulp的使用
  • 原文地址:https://www.cnblogs.com/pangblog/p/3268858.html
Copyright © 2011-2022 走看看