【数据结构学习记录15】——线索二叉树

一.概念

什么是线索二叉树?在我们写二叉树的时候,我们在末端结点的左右儿子用了NULL来表示,以告知结束。如果我们将这个左右儿子指向的地址指向下一个该遍历的结点,那岂不是就能提高结点的利用率?一个结点,在它的之前遍历的那个结点,叫这个结点的前驱,同理,在它之后遍历的第一个结点,叫做后继 。我觉得这幅图画得很好:
在这里插入图片描述
在这里插入图片描述

实质上来说,线索二叉树,能将一个二叉树变成一个双向链表。其中指向前驱和后记的指针,叫做线索, 加上线索的二叉树就叫线索二叉树

二.实现

为了实现我们的线索二叉树,我们要重新定义下一棵树:一棵树的儿子结点可以指向它真正的儿子,也就是左右结点。那么在线索二叉树下,它能指向下一个该遍历的结点。所以为了区分,我们的一棵树应该表示为:左儿子左儿子类型结点内容右儿子右儿子类型。其中儿子类型告知了这个儿子到底是指向的真儿子,还是说是是父辈的结点。

而且,线索二叉树一般由中序或者后序的方式创建。

我们按照中序的方式来遍历。该结点的前驱,应该是它遍历左左子后,堆栈的最顶层那个结点(毕竟是最后一个)。该结点的后去,应该是它遍历右儿子后的栈底的那个结点(第一个)。

三.代码实现

网上找的代码:

#include <stdio.h>
#include <stdlib.h>
 
#define ERROR  0
#define OK  1
 
typedef enum{
    
    Link, Thread} PointerTag;      //link = 0表示指向左右孩子指针
                                            //Thread = 1表示指向前驱或后继的线索
typedef struct BitNode
{
    
    
    char data;                              //结点数据
    struct BitNode *lchild;                 //左右孩子指针
    struct BitNode *rchild; 
    PointerTag ltag;                        //左右标志
    PointerTag rtag;
}BitNode, *BiTree;
 
BiTree pre;                                 //全局变量,始终指向刚刚访问过的结点
 
//前序创建二叉树
void CreateTree(BiTree *t)
{
    
    
    char ch;
    scanf("%c", &ch);
     
    if(ch == '#')
    {
    
    
        *t = NULL;
    }
    else
    {
    
    
        (*t) = (BiTree)malloc(sizeof(BitNode));
        if((*t) == NULL)
        {
    
    
            return;
        }
        (*t)->data = ch;
        CreateTree(&((*t)->lchild));
        CreateTree(&((*t)->rchild));
    }
}
 
 
//t指向头结点,头结点左链lchild指向根结点,头结点右链rchild指向中序遍历的最后一个结点。
//中序遍历二叉线索树表示的二叉树t
int InOrderThraverse_Thr(BiTree t)
{
    
    
    BiTree p;
    p = t->lchild;           //p指向根结点
    while(p != t)
    {
    
    
        while(p->ltag == Link)   //当ltag = 0时循环到中序序列的第一个结点
        {
    
    
            p = p->lchild;
        }
        printf("%c ", p->data);  //显示结点数据,可以更改为其他对结点的操作
        while(p->rtag == Thread && p->rchild != t)
        {
    
    
            p = p->rchild;
            printf("%c ", p->data);
        }
 
        p = p->rchild;           //p进入其右子树
    }
 
    return OK;
}
 
//中序遍历进行中序线索化
void InThreading(BiTree p)
{
    
    
    if(p)
    {
    
    
        InThreading(p->lchild);              //递归左子树线索化
        if(!p->lchild)                       //没有左孩子
        {
    
    
            p->ltag = Thread;                //前驱线索
            p->lchild = pre;             //左孩子指针指向前驱,这里是第3步
        }
        if(!pre->rchild)                 //没有右孩子
        {
    
    
            pre->rtag = Thread;              //后继线索
            pre->rchild = p;             //前驱右孩子指针指向后继(当前结点p)
        }
        pre = p;
 
        InThreading(p->rchild);              //递归右子树线索化
    }
}
//建立头结点,中序线索二叉树
int InOrderThread_Head(BiTree *h, BiTree t)
{
    
    
    (*h) = (BiTree)malloc(sizeof(BitNode));
    if((*h) == NULL)
    {
    
    
        return ERROR;
    }
 
    (*h)->rchild = *h;
    (*h)->rtag = Link;
 
    if(!t)      //如果为NULL
    {
    
    
        (*h)->lchild = *h;
        (*h)->ltag = Link;
    }
    else
    {
    
    
        pre = *h;
        (*h)->lchild = t;        //第一步
        (*h)->ltag = Link;
        InThreading(t);         //找到最后一个结点
        pre->rchild = *h;        //第四步
        pre->rtag = Thread;
        (*h)->rchild = pre;      //第二步
    }
}
 
int main(int argc, char **argv)
{
    
    
    BiTree t;
    BiTree temp;
 
    printf("请输入前序二叉树的内容:\n");
    CreateTree(&t);                 //建立二叉树
    InOrderThread_Head(&temp, t);       //加入头结点,并线索化
    printf("输出中序二叉树的内容:\n");
    InOrderThraverse_Thr(temp);
     
    printf("\n");
    return 0;
}

猜你喜欢

转载自blog.csdn.net/u011017694/article/details/110038523