线索二叉树

线索二叉树:我们把指向前驱和后继的指针称为线索,加上线索的二叉树,就是对应的线索二叉树。
线索化:对某二叉树以某种次序遍历使其变为线索二叉树的过程。
线索化二叉树的目的:把树中所有结点未利用的指针空间进行利用。减少空间浪费。
我们来具体看看线索化的过程:我们以这个图为例,进行中序遍历的线索化。
(中序遍历的顺序:HDIBJEAFCG)
在这里插入图片描述
在中序遍历的过程中,我们将所有空指针域中的rchild(右孩子指针),改为其后继结点。(对于二叉树的中序遍历可参考这一片博客:二叉树的遍历方法)于是,我们得到这样的树:
在这里插入图片描述
在遍历的同时,我们把所有**空指针域中的lchild(左孩子指针),改为其前驱结点。**于是:
在这里插入图片描述
我们把上面两个图综合一下:
在这里插入图片描述
那么我们现在会有疑问了,就有某个结点的lchild究竟是左孩子还是前驱结点?为了解决这个问题,我们在每个结点中再增设两个标志域ltag和rtag,他俩只是存放0和1数字的布尔型变量,因此其占用的内存空间要小于指针域。结点结构如图:
在这里插入图片描述

其中:
ltag为0时,指向左孩子,为1,指向前驱
rtag为0时,指向右孩子,为1,指向后继

因此这棵树就变成这样了:
在这里插入图片描述
现在我们代码实现一下线索二叉树的结构定义

typedef enum {
    Link,Thread
}PointerTag;//Link==0表示指向左右孩子指针。Thread==1表示指向前驱或后继。
typedef struct BiThrNode{
    TElemType data;     //结点数据
    struct BiThtNode *lchild *rchild;//左右孩子结点
    PointerTag LTag;
    PointerTag RTag;       //左右标志
}

线索化的实质就是将二叉树中的空指针改为前驱或后继的线索。由于前驱和后继的信息只有遍历该二叉树时才能得到,所以线索化的过程就是在遍历的过程中修改空指针的过程。
中序遍历线索化的函数代码:

BiThrTree  pre;//指向刚刚访问过的结点,为了表示前驱
void InThreading(BiThrTree p){
    if(p){
        InThreading(p->lchild);//递归左子树
        if(!p->lchild){
            p->LTag=Thread;//指定为前驱线索
            p->lchild=pre;//左孩子指针指向前驱
        }
        if(!pre->rchild){
           pre->Rtag=Thread;//指定为后继线索
           pre->rchild=p;//右孩子指针指向后继
       }
       pre=p;               //记录当前的p(也就是下一次的前驱)
       InThreading(p->rhild);//递归右子树
    }
}

两个递归过程中间代码的详解:
1.if(!p->lchild)表示如果某结点的左指针域为空,因为它的前驱结点刚刚访问过并且赋值给了pre,所以直接把pre赋值给p->lchild,并修改标志(p->LTag=Thread)(也就是定义为1)。完成前驱结点的线索化。
2.而后继就相对有一些麻烦,因为我们还没有访问到当前结点的后继,因此我们只能对上一个结点(前驱pre)进行右指针域(rchild)的判断。if(!pre->rchild)表示如果为空,而p是pre的后继,所以 pre->rchild=p ,并设置pre->Rtag=Thread,完成后继结点的线索化。
3.pre=p,它的作用就是记下当前的结点p,访问的下一个结点时,pre就是就是这一结点的前驱了。

我们对其线索化后,给其加一个头结点,并令其lchild指向二叉树的根结点,rchild指向中序遍历的最后一个结点。而遍历的第一个结点的lchild和最后一个结点的rchild指向头结点。
在这里插入图片描述
遍历中序线索二叉树的代码如下:

Status InOrderTraverse_Thr(BiThrTree T)
{
    BiThrTree p;
    p=T->lchild;    //p指向根结点
    while(p!=T){    //空树,或者遍历结束时 p==T
        while(p->LTag==Link){//当LTag==0时循环到中序序列的第一个结点
            p=p->child;
        }
        printf("%c",p->data);//进行数据操作
       while(p->RTag==Thread&&p->rchild!=T){
           p=p->rchild;
           printf("%c",p->data);//进行数据操作
       }
       p=p->rchild;         //p进右子树根
   }
   return OK;
}

代码讲解:
代码第四行:p=T->lchild;,进入树的根结点
代码第5-15行(第一个循环):while(p!=T),一直循环到p回到头结点时停止。也就是图中虚线4的出现
代码第6-8行:while(p->LTag = = Link),这就是有左孩子就继续往下走。如图中的A->B->D->H。
代码第10-13行:while(p->RTag= =Thread&&p->rchild!=T),这是打印后继结点的。
代码第14行:进入右孩子。

至此,线索二叉树就如这样,它利用了空指针域的空间(节省空间),同时一次遍历就可以终生受用前驱和后继信息(节省时间)。

参考:
大话数据结构/程杰 著.—北京:清华大学出版社,2011.6

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值