• 二叉树 遍历 推导 建立


    二叉树链式存储结构

      二叉树每个结点最多有两个孩子,所以设计一个数据域两个指针域。称这样的链表叫做二叉链表。

    其中data是数据域,lchild和rchild都是指针域,分别存放指向左孩子和右孩子的结点。

      二叉链表的结点结构定义代码:

    struct BiTNode
    {
        char ch;    //结点数据
        struct BiTNode *lchild;    //左孩子指针
        struct BiTNode *rchild;    //右孩子指针
    };

    结构示意如图:

    遍历二叉树

      二叉树的遍历是指从根结点出发,按照某种次序依次访问二叉树中所有结点,使得每个结点被访问一次且仅被访问一次。

    1.前序遍历

      二叉树为空,则空操作返回,否则先访问根结点,然后前序遍历左子树,再前序遍历右子树。 根 左 右

    先根A

        BDGH左树 --> 再根B --> DGH一颗树 --> 再根D --> 再G --> H

        CEIF右树 --> 再根C -->  EI一颗树 --> 再根E 左没有,右--> I --> F

        ABDGHCEIF

    2.中序遍历

      二叉树为空,则空操作返回,否则中序遍历根结点的左子树,然后访问根结点,最后中序遍历右子树。 左 根 右

    先左数BDGH 再A 最后右树 CEIF

    先左树B下面又是一颗树再左树D下面又是一颗树再左树G 下面没有左树了就返回根 G 右树没有 返回根D 右树H 左右子树没有 返回根B 右树没有返回根A  右树开始 先左树CEI 下面又是一颗树再左树E 左树没有返回根E 右树I没有左右树返回根C 右树F开始 没有左子树返回根F

    最终 GDHBAEICF

    3.后序遍历

      二叉树为空,则空操作返回,否则从左到右先叶子后结点的方式遍历访问左右子树,最后访问根结点。 左 右 根

      GHDBIEFCA

    二叉树前序遍历递归算法:

    void Recursion(struct BiTNode *root)
    {
        if (NULL == root)
         return; printf("%c ", root->ch); Recursion(root->lchild); //遍历左子树 Recursion(root->rchild); //遍历右子树 }

    二叉树中序遍历递归算法:

    void Recursion(struct BiTNode *root)
    {
        if (NULL == root)
            return;
    Recursion(root
    ->lchild); //遍历左子树 printf("%c ", root->ch); Recursion(root->rchild); //遍历右子树 }

    二叉树后序递归遍历算法:

    void Recursion(struct BiTNode *root)
    {
        if (NULL == root)
            return;
    
        Recursion(root->lchild); //遍历左子树
        Recursion(root->rchild); //遍历右子树
        printf("%c ", root->ch);
    }
    #define _CRT_SECURE_NO_WARNINGS
    
    #include<stdio.h>
    #include<stdlib.h>
    
    struct BiTNode
    {
        char ch;    //结点数据
        struct BiTNode *lchild;    //左孩子指针
        struct BiTNode *rchild;    //右孩子指针
    };
    void Recursion(struct BiTNode *root)
    {
        if (NULL == root)
            return;
    
        Recursion(root->lchild); //遍历左子树
        Recursion(root->rchild); //遍历右子树
        printf("%c ", root->ch);
    }
    
    void test()
    {
        struct BiTNode nodeA = { 'A',NULL,NULL };
        struct BiTNode nodeB = { 'B',NULL,NULL };
        struct BiTNode nodeC = { 'C',NULL,NULL };
        struct BiTNode nodeD = { 'D',NULL,NULL };
        struct BiTNode nodeE = { 'E',NULL,NULL };
        struct BiTNode nodeF = { 'F',NULL,NULL };
        struct BiTNode nodeG = { 'G',NULL,NULL };
        struct BiTNode nodeH = { 'H',NULL,NULL };
        struct BiTNode nodeI = { 'I',NULL,NULL };
    
        nodeA.lchild = &nodeB;
        nodeA.rchild = &nodeC;
        nodeB.lchild = &nodeD;
        nodeD.lchild = &nodeG;
        nodeD.rchild = &nodeH;
        nodeC.lchild = &nodeE;
        nodeC.rchild = &nodeF;
        nodeE.rchild = &nodeI;
    
        Recursion(&nodeA);
    }
    
    int main()
    {
        test();
    
        system("pause");
    } 

    推导遍历结果

      已知一颗二叉树的前序遍历序列为 ABCDEF,中序遍历序列为 CBAEDF,请问这颗二叉树后序遍历结果是多少?

    前序遍历先打印根再递归左右子树。所以前序遍历序列为ABCDEF,第一个字母是A被打印出来,就说明A是根结点的数据。中序遍历先递归左子树再打印根再递归右子树。中序遍历CBAEDF,可以知道C、B是A的左子树结点,E、D、F是A的右子树结点。

      然后看前序中C和B,它的顺序是ABCDEF,是先打印B后打印C,所以B应该是A的左孩子,而C就只能是B的孩子,此时是左还是右孩子不确定。再看中序CBAEDF,C是在B的前面打印,这就说明C是B的左孩子,否则就是右孩子(右孩子中序就是先打印B)。

      再看前序中的E、D、F,它的顺序是ABCDEF,那就意味着D是A结点的右孩子,E和F是D的子孙,注意,它们中有一个不一定是孩子,还有可能是孙子。再看中序序列CBAEDF,由于E在D的左侧,而F在右侧,所以可以确定E是D的做孩子,F是D的右孩子。因此最终得到二叉图:

    由图可知后序遍历结果: CBEFEA
    例二:已知一颗二叉树中序序列是ABCDEFG,后序序列式BDCAFGE,求前序序列。

    推理出结构图可知前序序列为:EACBDGF

    从这里得出两个二叉树遍历的性质:

      已知前序遍历序列和中序遍历序列,可以唯一确定一颗二叉树

     已知中序遍历序列和后序遍历序列,可以唯一确定一颗二叉树

    注意:已知前序和后序,是不确定一颗二叉树的。原因也很简单,比如前序序列ABC,后序序列CBA。我们可以确定A一定是根结点,但接下来无法知道那个结点是左子树,那个是右子树。这棵树可能如图所示:

    二叉树建立

    将二叉树中每个节点的空指针引出一个虚结点,其值为一特定值,比如“#”。我们称这种处理后的二叉树为原二叉树的扩展二叉树。假设二叉树的结点均为一个字符,把刚才前序遍历序列如AB#D##C##用键盘挨个输入。实现算法如下:

    #define _CRT_SECURE_NO_WARNINGS
    #include<stdio.h>
    #include<stdlib.h>
    typedef struct Node //二叉树链表结点结构定义
    {
        char data;    //结点数据
        struct Node *lchild;    //左孩子指针
        struct Node *rchild;    //右孩子指针
    }BiTNode,*BiTree;
    //按前序输入二叉树中结点的值(一个字符)
    //#表示空树
    void CreateBiTree(BiTree *T) 
    {  //为什么这里是struct ** (BiTree *)二级指针????????????
        char ch;
        scanf("%c", &ch);
        if (ch == '#')
            *T = NULL;
        else
        {
            *T = ( BiTNode *)malloc(sizeof(BiTNode));
            if (!*T)
                exit(-1);
    (
    *T)->data = ch; //生成根结点 CreateBiTree(& (*T)->lchild); //构建左子树 CreateBiTree(& (*T)->rchild); //构建右子树 } } void Recursion(BiTNode *root) //前序遍历 { if (NULL == root) return; printf("%c ", root->data); Recursion(root->lchild); //遍历左子树 Recursion(root->rchild); //遍历右子树 } int main() { BiTree T = NULL; CreateBiTree(&T); Recursion(T); //前序遍历打印结点出来 system("pause"); }

    当然,完全可以用中序或后序遍历的方式实现二叉树的建立,只不过代码里生成结点和构造左右子树代码顺序交换一下。

    另外,输入的字符也要做相应的更改。如上图扩展二叉树的中序遍历字符串应该为#B#D#A#C#,而后序字符串应该为###DB##CA。

  • 相关阅读:
    【Redis破障之路】四:Jedis基本使用
    【JVM进阶之路】十二:字节码指令
    Java学习之Spring MVC路由映射
    Java安全之FastJson JdbcRowSetImpl 链分析
    Allatori混淆动态调试分析
    Java工具开发手记
    Java安全之JBoss反序列化漏洞分析
    docker 应用篇————swarm[二十]
    docker 应用篇————docker-compose[十九]
    docker 应用篇————docker 自定义网络[十八]
  • 原文地址:https://www.cnblogs.com/Yang-Xin-Yi/p/14749390.html
Copyright © 2020-2023  润新知