线索二叉树的理解

为什么要有线索二叉树?

为了解决无法直接找到该结点在某种遍历序列中的前驱和后继结点的问题

但是同时出现了二叉链表找左、右孩子困难的问题,即在构建线索二叉树之后,链表的原来遍历方式会出问题

--ltag为0表示lchild指向该节点的左孩子,为1表示lchild指向该节点的前驱

--rtag为0,表示rchild指向该节点的右孩子,为1时,rchild指向该节点的后继

#include <stdio.h>
#include <stdlib.h>

typedef char ElemType;

/*
线索存储标志位
Link(0):表示指向左右孩子的指针
Thread(1):表示指向前驱后继的线索
*/
typedef enum {Link, Thread} PointerTag;

typedef struct BiThrNode{
    ElemType data;
    struct BiThrNode *lchild, *rchild;
    PointerTag ltag;
    PointerTag rtag;
}BiThrNode, *BiThrTree;

//全局变量,始终指向刚刚访问过的节点
 BiThrTree pre;

//创建一棵二叉树,约定用户遵照前序遍历方式输入数据
void createBiThrTree(BiThrTree *T){
    char c;
    scanf("%c", &c);
    if('#' == c){
        *T = NULL;
    }else{
        *T = (BiThrNode*)malloc(sizeof(BiThrNode));
        (*T)->data = c;
        (*T)->ltag = Link;
        (*T)->rtag = Link;
        createBiThrTree(&(*T)->lchild);
        createBiThrTree(&(*T)->rchild);
    }
}

//中序遍历线索化
void inThreading(BiThrTree T){
    if(T){
        inThreading(T->lchild);//递归左孩子线索华
        //节点处理
        if(!T->lchild){//处理前驱,什么时候能知道前驱,就是当前节点的时候,
//已经知道前一个是pre了,所以直接tag=thread,lchild=pre
            T->ltag = Thread;
            T->lchild = pre;
        }
        if(pre!=NULL && !pre->rchild){//处理后继,什么时候处理后继,只有访问到下一个的时候,
//才能知道下一个是谁,因为当访问下一个的时候,下一个是T,让pre的rchild指向T就好
            pre->rtag = Thread;
            pre->rchild = T;
        }
        pre = T;

        inThreading(T->rchild);//递归右孩子线索华
    }
}

//中序遍历线索二叉树---非递归
void InOrderThreading(BiThrTree T){
    BiThrTree p = T;
    while(p != NULL){
        //当ltag == Thread时,循环到中序序列第一个节点
        while(p->ltag == Link){
            p = p->lchild;
        }

        printf("%c ", p->data);
        while(p->rtag == Thread && p->rchild != NULL){
            p = p ->rchild;
            printf("%c ", p->data);
        }
        p = p->rchild;
    }

}

int main(){
    BiThrTree T = NULL;
    createBiThrTree(&T);
    inThreading(T);
    pre->rtag = Thread;
    InOrderThreading(T);
    return 0;

}

①前序输入构造二叉树

---输入c

---递归出口:如果c为#,那么表明该节点为空,*T = NULL;

---如果不等于#,表明该节点不为空

--------由于前序遍历,先根后左右,因此递归思想就是为当前节点分配空间,且左右tag都为link

--------然后递归左孩子

--------递归右孩子

②中序构造线索二叉树(参考中序遍历,如果T为空,递归出口,如果不为空,递归遍历当前节点左子树,然后打印出当前节点,递归遍历当前节点右子树,线索化只是把打印改成线索化操作)

---如果T为空,递归出口

---如果不为空,开始操作

------线索化当前T节点的左子树

------线索化是为了找到某个孩子为空的节点的前驱和后继,因此考虑什么时候能得到前驱,什么时候能得到后继

-----------处理前驱:针对当前节点,什么时候能得到前驱,访问当前节点的时候就可以,因此要记录上一个节点pre,不过前提是T的左子树为空;

-----------处理后继:针对pre节点,什么时候处理后继,访问下一个节点的时候,才能知道当前节点的后继是什么,因此对于下一个节点T来说,pre的后继就是T,因此要对pre进行操作,前提是pre!=NULL且pre的右子树为空

③中序遍历线索二叉树---非递归(左中右)

---得到根节点,将其给移动指针p

---如果p不为空,进行循环,因为线索化之后,相当于已经树给连接起来了,知道找到最后一个元素,那么他的地址就是空的

------循环到最左边的节点,如果ltag==link说明是真的有孩子,那么p=p->lchild

------输出该p节点的元素

------接下来后继,如果rtag为线索并且右子树不为空(担心右子树为空,还继续下去引起内存溢出),p=p->rchild;输出当前的p节点(用if也行,既然存在后继,即后继一定为某个父亲,而父亲只有两种情况,有没有右孩子,但不管有没有循环条件都不满足,即只执行一次)

------p=p->rchild’用来处理拥有右孩子的节点

猜你喜欢

转载自blog.csdn.net/huangwei18351/article/details/81835925