数据结构之线索二叉树

四、线索二叉树

遍历二叉树是以一定的规则将二叉树中的结点排列成一个线性序列,即二叉树的先序序列、中序序列或后序序列,在这些线性序列中,每个结点仅有一个直接前驱和直接后继。以二叉链表作为存储结构时,只能得到结点左、右子树的信息,而不能直接得到结点在某一序列中的前驱和后继,这些信息只能在遍历的动态过程中才能得到。其实n个结点的二叉链表中必定存在n+1个空链域,因此可以利用这些空链域来存放结点的前驱和后继。为此做如下规定:若结点有左子树,则lch域指向左子树,否则令lch域指向其前驱;若结点有右子树,则其rch域指向其右子树,否则令rch指向其后继。为了区分lch、rch域中的指针指向左子树和右子树或前驱和后继,结点结构增加了两个标志域。

Ltag=0  lchild域指向结点的左子树

Ltag=1  lchild域指向结点前驱

Rtag=0  lchild域指向结点的右子树

Rtag=1  lchild域指向结点的后继

其C语言描述如下:

/* Link==0:指针;Thread==1:线索*/

Typedef enum{Link,Thread} PointerTag;

Typedef struct BiThrNode
   {
    ElemenType data; // 数据域
    PointerTag ltag; // 左标志域
    int rtag; // 右标志域
    struct BiThrNode * lchild; // 左子树指针域 
    struct BiThrNode * rchild; // 右子树指针域
   }

这种结点结构构成的二叉链表作为二叉树的存储结构,成为线索链表,其中指向前驱和后继的指针称作线索。加上线索的二叉树成为线索二叉树。对二叉树以某种次序遍历使其成为线索二叉树的过程叫线索化。

对给定的线索二叉树中的某个节点p,查找节点p的后继(中序),其特点为所有叶子结点的右链直接指示了后继,所有非终端结点的后继应是其右子树中第一个中序遍历的结点。

对给定的线索二叉树的某个结点p,查找结点p的前驱(中序),其特点为若其左标志为“1”,则左链为线索,指示其前驱,否则其前驱为左子树上最后遍历的一个结点。

可见,对线索二叉树进行遍历可通过线索找到相应的前驱和后继,而无须递归进行。

线索二叉树根据遍历规则的不同,又可分为前序线索二叉树、中序线索二叉树、后序线索二叉树。下面我们以中序线索二叉树为例,来说明如何将一棵二叉树转化为中序线索二叉树。 
  要在二叉链表中生成中序遍历顺序的线索,只需在中序遍历过程中将访问根结点用下列操作代替:
   (1)若上次访问到的结点的右指针为空,则将当前访问到的结点序号填入,并置右标志为1。
  (2)若当前访问到的结点的左指针为空,则将上次访问到的结点序号填入,并置左标志为1。

  为了方便起见,仿照线性表的存储结构,在二叉树的线索链表上也添加一个头节点,并令其lchild域的指针指向二叉树的根结点,其rchild域的指针指向中序遍历时访问的最后一个结;然后,令二叉树中序序列中的第一个结点的lchild域指针和最后一个结点rchild域的指针均指向头结点。这好比为二叉树建立了一个双向线索链表,既可从第一个结点起顺后继进行遍历,也可从最后一个结点起顺前驱进行遍历。

1、建立线索树

中序遍历建立中序线索链表的算法描述如下:
  void inthread(root) // 这里的root是根结点,t是指向根结点的头结点
  struct BiThrNode * root; //定义root
  {
   struct BiThrNode *inthreading( ); //穿线
      struct BiThrNode *pre;
   struct BiThrNode *t; //定义t
   t=((struct BiThrNode *)malloc(sizeof(BiThrNode))); // 申请一个节点
   t->ltag=Link; //左标志域置为0
   t->rtag=Thread; //右标志与置为1
   t->rchild=t; //线索指向t
   if(root==NULL) 
      {
          t->lchild=t; //如果根结点非空,指向左子结点
       }
    else
    {
      pre=inthreading(&root); //穿线
     t->lchild=root; //左子结点指向root
     t->rchild=pre; //线索指向pre
     pre->rchild=t; //右子结点指向t
     pre->rtag=Thread; //右标志域置为1
    }
        invodth(&t);  // 调用中序线索树遍历算法
   }

   struct BiThrNode * inthreading(p) //穿线
     struct BiThrNode **p; //定义两级指针
     {
    if((*p)!= NULL) //如果(*p)非空
        { 
           inthreading(&((*p)->lchild)); // 对结点* p的左子树进行线索化
       if(((*p)->lchind)==NULL) //如果(*p)没有左子结点
       {
        (*p)->ltag=Thread; // 将该结点的左标志置1
        (*p)->lchild=pre; // 结点的左指针指向它的前驱
        }
       if(pre->rchild==NULL) //如果右子结点非空
       {
        pre->rtag=Thread;  // 将结点* p的前驱的右标志置1
        pre->rchild=*p; // 结点* p的前驱的右指针指向该结点
        }
        pre=*p; //赋值
        inthreading(&((* p)->rchild)); // 对结点* p的右子树进行线索化
    }
        return pre;
   }

2、  对于二叉树的遍历,如果要写出非递归算法,我们必须借助于一个栈。而如果此二叉树是线索二叉树,我们就能方便地写出非递归算法。
     对上面所生成的中序线索树进行遍历的算法描述如下:

void invodth(h)    // 这里的h是指向头结点t的指针
  struct BiThrNode **h; //定义两级指针
  {
    struct BiThrNode *p; //定义p
    p=(*h)->lchild; // p得到根结点root的值
    while(p!=(*h)) //p不等于(*h)
    {
      while (p->ltag==Link) //左子结点是指针
      p=p->lchild; //指向左子结点
      if (p->ltag==Thread) printf("%c", p->data); //如果左子结点是线索,输出结点值
      while(p->rtag==Thread&&p->rchild!=(*h)) //右子结点是线索且右子结点不等于(*h)
      {
        p=p->rchild; //指针指向右子结点
        printf("%c", p->data); //输出结点值
      }
    p=p->rchild; //指针指向右子结点
    }
  }

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值