6.5.2 以中序线索链表为存储结构的中序遍历

  如何在中序线索链表上进行遍历,关键问题有二:
   一是如何找到访问的第一个结点?
   二是如何找到每个结点在中序序列中的后继?

  首先,在中序线索链表上如何找到中序序列中的第一个结点?
  根据6.4.1节中对二叉树的中序遍历的定义可知,中序遍历访问的第一个结点必定是 "其左子树为空"的结点。若根结点没有左子树,则根结点即为中序遍历访问的第一个结点,若根结点的左子树不空,则访问的第一个结点应该是其左子树中"最左下的结点",即从根结点出发,顺指针 Lchild 找到其左子树直至某个结点的指针 Lchild 为"线索"止,该结点必为中序序列中的第一个结点。如右图所示例子中的"结点D"。

  如何在中序线索链表中找结点的后继?
  若结点没有右子树,即结点的右指针类型标志 Rtag 为"Thread",则指针 Rchild 所指即为它的后继,如右图所示例子中的"结点D"、"结点G"等,若结点有右子树,则它的后继应该是其右子树中访问的第一个结点。和前面所述找二叉树的第一个结点一样,就应该从它的右子树根出发,顺指针 Lcuild 直至没有左子树的结点为止,该结点即为它的后继。例如右图中结点B的后继为结点G。

算法6.9
  void InOrderTraverse_Thr(BiThrTree Thead,void (*Visit)(ElemType e))
 {
  // Thead 指向中序线索链表中的头结点,头结点的左指针 Lchild
  // 指向二叉树的根结点,头结点的右线索 Rchild 指向中序遍历
  // 访问的最后一个结点。本算法对此二叉树进行中序遍历,对
  // 树中每个数据元素调用函数 Visit 进行访问操作

  p = Thead->Lchild;            // p 指向二叉树的根结点
  while (p!= Thead) {          // 空树或遍历结束时,p==Thead
   while (p->LTag==Link) p = p->Lchild;
   Visit(p->data);            // 访问其左子树为空的结点
   while (p->RTag==Thread && p->Rchild!=Thread) {
    p = p->rchild; Visit(p->data);   // 访问"右线索"所指后继结点
   } // while
   p = p->Rchild;             // p 进至其右子树根
  } // while
 } // InOrderTraverse_Thr
   
 
  以线索链表作存储结构,即以指向线索链表中头结点的指针表示二叉树。由于在线索链表中已经包含有遍历得到的访问次序序列的信息,则在线索链表中进行遍历可以递推进行。即首先找到遍历中应该访问的"第一个"结点,而后接着顺每个结点的"后继"依次进行访问。在此仅以中序线索链表为例进行讨论。

 

  因为结点B的 RTag=Link,则顺指针 Rchild 找到它的右子树根(结点E),因为结点E的 LTag=Link,则顺指针 Lchild 找到它的左子树根(结点G),而因为结点 G 的 Ltag=Thread,说明G是结点B的右子树中"最左下" 的结点,所以结点G是结点B的后继。


  读者可以观看对上图的中序线索链表进行遍历的过程加深对算法6.9的理解。动画

  可见,在线索化链表上进行遍历省去了递归栈。

  其它序线索链表的遍历算法也类似,关键在于找后继结点,只是在后序线索链表上找后继还需要知悉结点"双亲"的的位置。