您的位置:首页 > 理论基础 > 数据结构算法

数据结构之线索二叉树

2015-09-09 23:36 330 查看
四、线索二叉树

遍历二叉树是以一定的规则将二叉树中的结点排列成一个线性序列,即二叉树的先序序列、中序序列或后序序列,在这些线性序列中,每个结点仅有一个直接前驱和直接后继。以二叉链表作为存储结构时,只能得到结点左、右子树的信息,而不能直接得到结点在某一序列中的前驱和后继,这些信息只能在遍历的动态过程中才能得到。其实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; //指针指向右子结点

    }

  }
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: