先序,中序线索二叉树的建立

二叉树的 遍历方式有4种,前序线索二叉树,中序线索二叉树,后序线索二叉树,层序线索二叉树。此处使用中序线索二叉树举例,其他三种建立方式类似,只是代码位置不同。

线索化的意义:                                                                                                                                     如果一个二叉树通过中序遍历结果为 a d f g b c;这是通过递归调用得到的遍历结果,线索化就是再遍历一遍,将所有结点连接起来,构成一个 链表,之后就可以通过遍历链表进行遍历访问,不必使用递归遍历.

重点1线索二叉树传递进来的指针要以引用的方式,否则改变指针的指向,改变的只是临时指针的指向。(凡是要更改指针指向的都传递指针的引用,不能是传递指针作为参数)

重点2:不论是前, 中, 后序线索化二叉树, 都需要对最后一个结点单独处理,处理最后结点, 是在新的函数中单独处理,不能在线索化函数中处理,因为线索化是调用自身递归,当利用循环线索化结束时候,pre和p同时指向了最后一个结点,此时单独处理最后一个结点 也就是pre->rchild=NULL;  pre->rtag = 1;

重点3: 前,中,后序线索化的二叉树不一样。(前序是根据前序遍历得到的结果,将结果连接起来,中序线索化是将中序遍历得到的结果,将结果用指针连起来,所以前,中,后线索化的结果不同)                                                                               

前 中 后序线索化的区别:

1. 前序线索化需要使用 ltag进行判断,因为p->lchild=NULL时, 让p->lchild=NULL;  根据前序遍历规则,p结点访问结束,现在需要对p->lchild孩子进行递归,但是此时p->lchild已经指向了前一个结点,就会导致在前一个结点和这个结点往复循环,导致不能线索化后面的结点

2.中序和后序线索化不需要使用ltag和rtag进行判断,因为根据中序和后序规则, 当访问p结点时候,它的左孩子(或者说前驱已经访问过了),不会造成往复循环,前序是可以指向前驱,再递归进来访问的是前驱的孩子,再指向前驱,再递归前驱的孩子,一直往复循环.

//创建线索二叉树结点结构
class node{
    public:
        char data;
        node *lchild,*rchild;
        int ltag,rtag;
};

 当ltag=0和rtag=0的时候,表示*lchild和*rchild含义不变,任然指向他们的左孩子和右孩子;

当ltag=1和rtag=1时候,表示*lchild和*rchild不再指向他们的左右孩子了,

而是lchild指向结点的前驱,rchild指向结点的后继

现在有如下图的二叉树,中序序列:  D G B A E C F

经过线索二叉树后,所有结点,如果左孩子为空,就让左孩子指向前驱,如果右孩子为空,

就让右孩子指向后继.

中序序列:  D G B A E C F  //可以看出G的左孩子指向D,G的右孩子指向B

当最后一个结点F->rchild应该指向NULL,但右孩子是用pre来控制的,pre无法到达最后的结点F,当最后一次循环,利用循环线索化结束后,需要手动对最后一个结点进行处理,即此时pre和p都指向了最后一个结点,需要手动设置pre->rchild = NULL, 同时手动设置pre->rtag=1

 先创建一个二叉树,并且把所有的ltag和rtag设置成0;

创建二叉树的方式(可以看下面链接这篇文章):

前序建立二叉树的两种方式_DL猿-CSDN博客

 创建完二叉树后,开始遍历二叉树,在遍历的过程中,将二叉树线索化。

从开始遍历建立线索二叉树只做下面几件事.

p是当前结点,pre是p的前一个结点(pre是一个全局变量).

1.判断p的左孩子是否为空,若为空,设置前驱线索,执行下面代码,如果不为空,不做任何操作.

       1.1 p->ltag=1;     //设置左线索为1

        1.2 p->lchild = pre;   //左孩子为空,就让左孩子指向前一个结点

2.判断pre是否为空,如果为空,则设置后继.执行下面代码,如果不为空,不做任何操作.

        2.1 pre->rtag=1;       //设置右线索为1

        2.2 pre->rchild = p;  //右孩子为空,则让右孩子指向下一个结点

3.移动pre结点,并且让p移动到新的结点  

        pre = p;   //让pre指向p

(pre指针有三种定义方式,1.将pre定义成全局变量,2.将pre定义成属性  3.将pre定义成静态变量)

        inThread(p->rchild)   ///右子树线索化

整体代码

//定义线索二叉树结构
class ThreadBitree
{
    public:
        node *root;  //定义一个根节点
        node *pre;  //定义pre指针用于记录前一个结点的位置

        ThreadBitree()
        {
            pre = NULL;
            creat(root)   //因为是引用的方式,所以可以直接操作根节点root
            inThread(root);  //调用线索化函数将二叉树线索化
        }

        //中序遍历线索化二叉树
        //实际就是一个中序遍历二叉树,只是把访问结点改了.
        inThread(node* &p)   //利用引用可以修改指针的内容
        {
            if(p==NULL)      //如果结点为空,则返回. 否则根据中序,进入左子树
            return;
        
        inThread(p->lchild);  //递归进入左子树将左子树线索化 当为空的时候返回来,执行下面代码

        //ltag=0表示有左孩子, ltag=1,表示有前驱
        if(p->lchild == NULL)
        {
            P->ltag=1;
            p->lchild = pre;
        }
        if(pre != NULL && pre->rchild == NULL)
        {
            pre->rtag = 1;
            pre->rchild = p;
        }
        
        //更新pre和p的值,让pre=p,p->rchild作为参数在赋值给右子树,
        //当进入新一层函数时候,pre记录的是上一层的地址,p记录的是本次的地址,满足线索树的需求
        

        pre = p;             //先让pre记录当前结点,再让p进入下一结点。
        inThread(p->rchild)  //右子树线索化
}
        void creat(node* &bt); //用于创建二叉树,这里需要ltag和rtag设置成0

前序创建线索二叉树,

如图:  

前序线索化递归的时候需要用两个if语句对ltag和rtag进行判断,比如:                                            if(ltag==0)表明它有左子树,则将左子树传入递归,如果不进行判断,此时左孩子已经指向了前驱,再将左孩子传进去递归,则会一直在这两个结点间循环递归,跳不出去循环,如果使用if(ltag==0) 表明是它自己的左孩子,那么就递归它的左孩子,如果ltag==1,表示是它的前驱,则不进行递归。如果左右孩子都为NULL,那么就返回到上一层调用的位置继续执行调用位置下面的代码. 

 inThread(node* &p)   //使用引用可以修改指针的内容 
        {
            if(p==NULL)      //如果结点为空,则返回. 否则根据中序,进入左子树
                return;
        

        if(p->lchild == NULL)
        {
            P->ltag=1;
            p->lchild = pre;
        }
        if(pre != NULL && pre->rchild == NULL)
        {
            pre->rtag = 1;
            pre->rchild = p;
        }
        
        //更新pre和p的值,让pre=p,p->rchild作为参数在赋值给右子树,
        //当进入新一层函数时候,pre记录的是上一层的地址,p记录的是本次的地址,满足线索树的需求
        

        pre = p;             //先让pre记录当前结点,再让p进入下一结点。
        
        //先序需要看ltag和rtag,当ltag或rtag=1时候,说明该结点被线索化了,就不需要再线索化
        if(p->ltag == 0){
            inThread(p->lchild);
        }
        
        if(p->rtag==0){
            inThread(p->rchild)  //右子树线索化
        }

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值