zoukankan      html  css  js  c++  java
  • 数据结构-线索化二叉树

    对于一具有n个节点的二叉树来说,采用二叉链存储结构时,每个节点有两个指针域总共有2n个指针域,而只有n-1个节点被有效指针所指(n个节点只有根节点没有被有效指针域所指)

    故一共有n+1个空指针域,造成空间上的浪费。

    对二叉树遍历产生的序列是个线性序列。如果用这些空链域存放指向节点的前驱节点和后驱节点,这样遍历时会方便许多。

    先放个链接:中序线索化二叉树的动画过程。方便后面算法的理解。

    由于遍历的方式不同,产生的遍历线性序列不同。重新定义二叉树的节点如下

    typedef struct node
    {    ElemType data;          //节点的数据域
        int ltag,rtag;            //增加的线索标记
        struct node *lchild;    //当ltag=0时lchild代表左节点,ltag=1时代表没有左节点lchild代表的是前驱节点
        struct node *rchild;    //当rtag=0时rchild代表右节点,rtag=1时代表没有右节点rchild代表的是后继节点
    } TBTNode;//线索数的节点类型定义

    为了算法的设计方便我们在线索化二叉树上再增加一个头节点。头节点的data域为空,lchild指向没有线索时的根节点,将ltag设为0,而rchild指向某种遍历方式中的最后一个节点,rtag=1。

    对同一棵树的遍历方式不同,所得的线索树也不同。二叉树有线序中序后序三种,故线索树也有先序线索树,中序线索树,后序线索树三种。

    建立线索树的实质其实就是遍历一颗二叉树,在遍历的过程中检查当前节点的指针域是否为空,为空将它们改成前驱节点或者是后驱节点。

    而在三种线索化二叉树中

    先序线索二叉树查找一个节点的先序后继节点很简单如图先序线索树 假设当前节点为p,当p->rtag==1时我们知道p->rchild表示后继节点,此时只需判断是否指向了头节点root若是代表p就是先序遍历中的最后一个节点。而当p->rtag==0若p->ltag==1则p的后继节点就是p->rchild否则后继节点是p->lchild。

    先序遍历查找一个节点的先序前驱节点就不好办了,从下图先序线索树你可以看到求B、C的前驱节点在不知道双亲节点的情况下无法判断,对于DGEF四个点则是可以在不知双亲节点的情况下求得。

    image

    后序线索树存在同样的问题。查找后序前驱结点简单但查找查找后序后继节点也不大好办。

    所以在实际应用中先序线索化和后序线索化用的少,中序线索化用的比较多。中序线索化中求前驱节点和后继节点的方法如下。

    image

    image

    中序线索化程序单步调试过程

    从下图知道节点D左孩子指向了头节点即有D->lchild==root,而D->ltag=1,由此我们知道D没有前驱节点,而D->rchild=G!=root且D->rtag==0而对于D的右子树而言只有一个节点G所以G是D的后序节点否则应该是D的右子树中序遍历下的第一个节点是D的后继节点。

    image

    完整代码:

    //文件名:exp7-5.cpp
    #include <stdio.h>
    #include <malloc.h>
    #define MaxSize 100
    typedef char ElemType;
    typedef struct node
    {    ElemType data;          //节点的数据域
        int ltag,rtag;            //增加的线索标记
        struct node *lchild;    //当ltag=0时lchild代表左节点,ltag=1时代表没有左节点lchild代表的是前驱节点
        struct node *rchild;    //当rtag=0时rchild代表右节点,rtag=1时代表没有右节点rchild代表的是后继节点
    } TBTNode;//线索数的节点类型定义
    //创建二叉树
    void CreateTBTNode(TBTNode * &b,char *str)
    {    TBTNode *St[MaxSize],*p=NULL;
        int top=-1,k,j=0;  
        char ch;
        b=NULL;                //建立的二叉树初始时为空
        ch=str[j];
        while (ch!='')    //str未扫描完时循环
        {    switch(ch) 
            {
            case '(':top++;St[top]=p;k=1; break;        //为左节点
            case ')':top--;break;
            case ',':k=2; break;                    //为右节点
            default:p=(TBTNode *)malloc(sizeof(TBTNode));
                    p->data=ch;p->lchild=p->rchild=NULL;
                    if (b==NULL)                    //*p为二叉树的根节点
                        b=p;
                    else                            //已建立二叉树根节点
                    {    switch(k) 
                        {
                        case 1:St[top]->lchild=p;break;
                        case 2:St[top]->rchild=p;break;
                        }
                    }
            }
            j++;
            ch=str[j];
        }
    }
    //输出二叉树
    void DispTBTNode(TBTNode *b)
    {    if (b!=NULL)
        {    printf("%c",b->data);
            if (b->lchild!=NULL || b->rchild!=NULL)
            {    printf("(");
                DispTBTNode(b->lchild);
                if (b->rchild!=NULL) printf(",");
                DispTBTNode(b->rchild);
                printf(")");
            }
        }
    }
    TBTNode *pre;                        //全局变量
    void Thread(TBTNode *&p)
    {    if (p!=NULL)
        {    Thread(p->lchild);            //左子树线索化
            if (p->lchild==NULL)        //前驱线索
            {    p->lchild=pre;            //建立当前节点的前驱线索
                p->ltag=1;
            }
            else p->ltag=0;
            if (pre->rchild==NULL)        //后继线索
            {    pre->rchild=p;            //建立前驱节点的后继线索
                pre->rtag=1;
            }
            else pre->rtag=0;
            pre=p;
            Thread(p->rchild);            //右子树线索化
        }
    }
    //产生中序线索二叉树
    TBTNode *CreateThread(TBTNode *b)        //中序线索化二叉树
    {    TBTNode *root;
        root=(TBTNode *)malloc(sizeof(TBTNode));    //创建根节点
        root->ltag=0;root->rtag=1;
        root->rchild=b;
        if (b==NULL)                //空二叉树
            root->lchild=root;
        else
        {    root->lchild=b;
            pre=root;                //pre是*p的前驱节点,供加线索用
            Thread(b);                //中序遍历线索化二叉树
            pre->rchild=root;        //最后处理,加入指向根节点的线索
            pre->rtag=1;
            root->rchild=pre;        //根节点右线索化
        }
        return root;
    }
    void InOrder(TBTNode *tb)        //被ThInOrder算法调用
    {
        if (tb->lchild!=NULL && tb->ltag==0)    //有左孩子
            InOrder(tb->lchild);
        printf("%c ",tb->data);
        if (tb->rchild!=NULL && tb->rtag==0)    //有右孩子
            InOrder(tb->rchild);
    }
    void ThInOrder(TBTNode *tb)        //中序递归算法
    {
        InOrder(tb->lchild);
    }
    void ThInOrder1(TBTNode *tb)    //中序非递归算法
    {    TBTNode *p=tb->lchild;        //指向根节点
        while (p!=tb)
        {    while (p->ltag==0) p=p->lchild;
            printf("%c ",p->data);
            while (p->rtag==1 && p->rchild!=tb)
            {    p=p->rchild;
                printf("%c ",p->data);
            }
            p=p->rchild;
        }
    }
    void DestroyTBTNode1(TBTNode *tb)    //被DestroyTBTNode算法调用
    {
        if (tb!=NULL)
        {
            if (tb->lchild!=NULL && tb->ltag==0)  //有左孩子  
                DestroyTBTNode1(tb->lchild);
            if (tb->rchild!=NULL && tb->rtag==0)  //有右孩子  
                DestroyTBTNode1(tb->rchild);
            free(tb);
        }
    }
    void DestroyTBTNode(TBTNode *tb)    //释放中序线索二叉树的所有节点
    {
        DestroyTBTNode1(tb->lchild);
        free(tb);
    }
    void main()
    {    TBTNode *b,*tb;
        CreateTBTNode(b,"A(B(D,E(H(J,K(L,M(,N))))),C(F,G(,I)))");     
        printf("二叉树:");DispTBTNode(b);printf("
    ");
        tb=CreateThread(b);
        printf("线索中序序列:
    ");
        printf("    递归算法:");ThInOrder(tb);printf("
    ");
        printf("  非递归算法:");ThInOrder1(tb);printf("
    ");
        DestroyTBTNode(tb);
    }

    线索

    作者:leemoaly
    本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
  • 相关阅读:
    (七) rest_framework GenericAPIView/GenericViewSet/ ModelViewSet 解析
    (六) rest_framework 普通分页与加密分页
    (五) rest_framework 序列化与解析器源码实现
    (四) rest_framework 版本控制源码
    【使用 PySpark 分析 CSV 文件】
    安装 HBase1.3.6 on Windows 10 64 bit
    Spark Job 性能调优 (二)
    Spark RDD 分区到底怎么用?
    安装 Spyder python 开发环境 用于 Spark 数据分析 -word count
    安装 Spark on Windows 使用 PySpark
  • 原文地址:https://www.cnblogs.com/kavs/p/5002752.html
Copyright © 2011-2022 走看看