【树】线索二叉树的基本概念及遍历

基本概念

我们知道,在有 n 个结点的二叉链表中共有 2n 个链域,但只有 n-1 个有用的非空链域,其余 n+1 个链域是空的。可以利用剩下的 n+1 个空链域来存放遍历过程中结点的前驱和后继信息。

现作如下规定:

  • 若结点有左子树,则其 LChild 域指向其左孩子,否则 LChild 域指向其前驱结点;
  • 若结点有右子树,则其 RChild 域指向其右孩子,否则 RChild 域指向其后继结点。

为了区分孩子结点和前驱、后继结点,为结点结构增设两个标志域,如下图所示:
在这里插入图片描述

在这里插入图片描述

二叉树的线索化

线索化实质是将二叉链表中的空指针域,填上相应结点的遍历前驱或后继结点的地址,而前驱和后继的地址只能在动态的遍历过程中才能得到。因此线索化的过程即为在遍历过程中修改空指针域的过程。

对二叉树按照不同的遍历次序进行线索化,可以得到不同的线索二叉树,包括先序线索二叉树、中序线索二叉树和后序线索二叉树。这里重点介绍中序线索化的算法。

【算法思想】
(1)中序线索化采用中序递归遍历算法框架。
(2)加线索操作就是访问结点操作。
(3)加线索操作需要利用刚访问过结点与当前结点的关系,因此设置一个指针 pre,始终记录刚访问过的结点,其操作如下:
①如果当前遍历结点 root 的左子域为空,则让左子域指向 pre ;
②如果前驱 pre 的右子域为空,则让右子域指向当前遍历结点 root;
③为下次做准备,当前访问结点 root 作为下一个访问结点的前驱 pre。
【算法描述】

/* 对 root 所指的二叉树进行中序线索化,其中 pre 始终指向刚访问过的结点,其初值为NULL*/
void Inthread(BiTree root)
{
    if (root != NULL)
    {
        Inthread(root->LChild); /* 线索化左子树 */
        if (root->LChild == NULL)
        {
            root->Ltag = 1;
            root->LChild = pre;
            / *置前驱线索 * /
        }
        if (pre != NULL && pre->RChild == NULL) /* 置后继线索 */
        {
            pre->RChild = root;
            pre->Rtag = 1;
        }
        pre = root;                /*当前访问结点为下一个访问结点的前驱*/
            Inthread(root->RChild); /*线索化右子树*/
    }
}
在线索二叉树中找前驱,后继结点

以中序线索二叉树为例,来讨论如何在线索二叉树中查找结点的前驱和后继。
(1)找结点的中序前驱结点
根据线索二叉树的基本概念和存储结构可知,对于结点 p,

  • 当 p->Ltag=1 时,p->LChild 指 向 p 的前驱。
  • 当 p->Ltag=0 时,p->LChild 指向 p 的左孩子。由中序遍历的规律可知,作为根 p 的前驱结点,它是中序遍历 p 的左子树时访问的最后一个结点,即左子树的“最右下端”结点。

【算法描述】

BiTNode *InPre(BiTNode *p)
/* 在中序线索二叉树中查找 p 的中序前驱, 并用 pre 指针返回结果 */
{
    if (p->Ltag == 1)
        pre = p->LChild; /*直接利用线索*/
    else
    { /* 在 p 的左子树中查找“最右下端”结点 */
        for (q = p->LChild; q->Rtag == 0; q = q->RChild)
            ;
        pre = q;
    }
    return (pre);
}

在这里插入图片描述
(2)在中序线索树中找结点后继对于结点 p,若要找其后继结点,

  • 当 p->Rtag=1 时,p->RChild 即为 p 的后继结点;
  • 当 p->Rtag=0时,说明 p 有右子树,此时 p 的中序后继结点即为其右子树的“最左下端”的结点。

【算法描述】

BiTNode *InNext(BiTNode *p)
/*在中序线索二叉树中查找 p 的中序后继结点,并用 Next 指针返回结果*/
{
    if (p->Rtag == 1)
        Next = p->RChild; /*直接利用线索*/
    else
    { /*在 p 的右子树中查找“最左下端”结点*/
        if (p->RChild != NULL)
        {
            for (q = p->RChild; q->Ltag == 0; q = q->LChild)
                ;
            Next = q;
        }
        else
            Next = NULL;
    }
    return (Next)
}

在先序线索树中找结点的后继比较容易,根据先序线索树的遍历过程可知,

  • 若结点 p 存在左子树,则 p 的左孩子结点即为 p 的后继;
  • 若结点 p 没有左子树,但有右子树,则 p 的右孩子结点即为 p 的后继;
  • 若结点 p 既没有左子树,也没有右子树,则结点 p 的 RChild 指针域所指的结点即为 p 的后继。

用语句表示则为

if (p->Ltag==0) Next=p->Lchild; 
else Next =p->RChild;

同样,在后序线索树中查找结点 p 的前驱也很方便。

在先序线索树中找结点的前驱比较困难。

  • 若结点 p 是二叉树的根,则 p 的前驱为空;
  • 若 p是其双亲的左孩子,或者 p 是其双亲的右孩子并且其双亲无左孩子,则 p 的前驱是 p 的双亲
    结点
  • 若 p 是双亲的右孩子且双亲有左孩子,则 p 的前驱是其双亲的左子树中按先序遍历时最后访问的那个结点。
遍历中序线索树

遍历线索树的问题可以分解成两步,第一步是求出某种遍历次序下第一个被访问结点;然后连续求出刚访问结点的后继结点,直至所有的结点均被访问。以遍历中序线索树为例。
(1)在中序线索树上求中序遍历的第一个结点
【算法描述】

BiTNode *InFirst(BiTree Bt)
{
    BiTNode *p = Bt;
    If(!p) return (NULL);
    while (p->LTag == 0)
        p = p->Lchild;
    return p;
}

(2)遍历中序二叉线索树:
通过调用 InFirst 和 InNext ,可以实现对中序线索树的中序遍历,且不需要使用递归栈。函数 TInOrder 实现了这种遍历

void TInOrder(BiTree Bt)
{
    BITNode *p;
    P = InFirst(Bt);
    While(p)
    {
        Visit(p);
        p = InNext(p);
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值