線索二叉樹


線索二叉樹 (threaded binary tree)

                     

上圖所示的二叉鏈表,存在多個空指針域。假設一個二叉鏈表的結點數為n,則共有2n個指針域。而n個結點的二叉樹共有n-1條分支。所以空指針域的個數為:2n - (n-1) = n+1。

可以在這n+1個空指針域中保存結點的(以先序、中序或后序遍歷的)前驅和后繼指針,這樣在下次遍歷時,可以大大提高速度。

將所有空指針域中的rchild指向它的后繼。

 

將所有空指針域中的lchild指向它的前驅。

 

線索二叉樹(保留遍歷時結點在任一串行的前驅和后繼的信息):若結點有左子樹,則其lchild域指示其左孩子,否則令lchild域指示其前驅;若結點有右子樹,則其rchild域指示其右孩子,否則令rchild指示其后繼。

還需在結點結構中增加兩個標志域LTag和RTag。LTag=0時,lchild域指示結點的左孩子,LTag=1時,lchild域指示結點的前驅;RTag=0時,rchild域指示結點的右孩子,RTag=1時,rchild域指示結點的后繼。

以這種結點結構構成的二叉鏈表作為二叉樹的存儲結構,叫做線索鏈表,其中指向結點前驅和后繼的指針叫做線索,加上線索的二叉樹稱為線索二叉樹。對二叉樹以某種次序遍歷使其變為線索二叉樹的過程叫做線索化。若對二叉樹進行中序遍歷,則所得的線索二叉樹稱為中序線索二叉樹,線索鏈表稱為為中序線索鏈表。

 

二叉線索存儲表示

二叉樹的二叉線索存儲表示(以中序為例):在線索鏈表上添加一個頭結點,並令其lchild域的指針指向二叉樹的根結點,其rchild域的指針指向中序遍歷時訪問的最后一個結點。令二叉樹中序串行中的第一個結點的lchild域指針和最后一個結點的rchild域的指針均指向頭結點,這樣就創建了一個雙向線索鏈表。這樣定義的好處是既可以從第一個結點起順后繼進行遍歷,也可以從最后一個結點起順前驅進行遍歷。

 

/* 二叉樹的二叉線索存儲表示 */
 typedef enum{Link,Thread}PointerTag; /* Link(0):指針,Thread(1):線索 */
 typedef struct BiThrNode
 {
   TElemType data;
   struct BiThrNode *lchild,*rchild; /* 左右孩子指針 */
   PointerTag LTag,RTag; /* 左右標志 */
 }BiThrNode,*BiThrTree;

中序遍歷線索化的遞歸函數如下:

BiThrTree pre; /* 全局變量,始終指向剛剛訪問過的結點 */
 void InThreading(BiThrTree p)
 { /* 通過中序遍歷進行中序線索化,線索化之后pre指向最后一個結點。*/
   if(p) /* 線索二叉樹不空 */
   {
     InThreading(p->lchild); /* 遞歸左子樹線索化 */

     if(!p->lchild) /* 沒有左孩子 */
     {
       p->LTag=Thread; /* 左標志為線索(前驅) */
       p->lchild=pre; /* 左孩子指針指向前驅 */
     }

     if(!pre->rchild) /* 前驅沒有右孩子 */
     {
       pre->RTag=Thread; /* 前驅的右標志為線索(后繼) */
       pre->rchild=p; /* 前驅右孩子指針指向其后繼(當前結點p) */
     }

     pre=p; /* 保持pre指向p的前驅 */

     InThreading(p->rchild); /* 遞歸右子樹線索化 */
   }
 }

/* 結合上面的文字介紹和示意圖看 */
void InOrderThreading(BiThrTree *Thrt,BiThrTree T)
 { /* 中序遍歷二叉樹T,並將其中序線索化,Thrt指向頭結點。 */
   *Thrt=(BiThrTree)malloc(sizeof(BiThrNode));
   if(!*Thrt) /* 生成頭結點不成功 */
     exit(OVERFLOW);
   (*Thrt)->LTag=Link; /* 建頭結點,左標志為指針 */
   (*Thrt)->RTag=Thread; /* 右標志為線索 */
   (*Thrt)->rchild=*Thrt; /* 右指針回指 */
   if(!T) /* 若二叉樹空,則左指針回指 */
     (*Thrt)->lchild=*Thrt;
   else
   {
     (*Thrt)->lchild=T; /* 頭結點的左指針指向根結點 */
     pre=*Thrt; /* pre(前驅)的初值指向頭結點 */
     InThreading(T); /* 中序遍歷進行中序線索化,pre指向中序遍歷的最后一個結點 */
     pre->rchild=*Thrt; /* 最后一個結點的右指針指向頭結點 */
     pre->RTag=Thread; /* 最后一個結點的右標志為線索 */
     (*Thrt)->rchild=pre; /* 頭結點的右指針指向中序遍歷的最后一個結點 */
   }
 }

 

下面是中序遍歷線索二叉樹T的非遞歸算法:
void InOrderTraverse_Thr(BiThrTree T,void(*Visit)(TElemType))
 { /* 中序遍歷線索二叉樹T(頭結點)的非遞歸算法。*/
   BiThrTree p;
   p=T->lchild; /* p指向根結點 */

   while(p!=T)
   { /* 空樹或遍歷結束時,p==T */
     while(p->LTag==Link) /* 由根結點一直找到二叉樹的最左結點 */
       p=p->lchild;
     Visit(p->data); /* 訪問此結點 */

     while(p->RTag==Thread&&p->rchild!=T) /* p->rchild是線索(后繼),且不是遍歷的最后一個結點 */
     {
       p=p->rchild;
       Visit(p->data); /* 訪問后繼結點 */
     }

     p=p->rchild; /* 若p->rchild不是線索(是右孩子),p指向右孩子,返回循環,*/
   }              /* 找這棵子樹中序遍歷的第1個結點 */
 }

Reference:

[1] 《大話數據結構》

[2] wikipedia(二叉樹):http://zh.wikipedia.org/wiki/%E4%BA%8C%E5%8F%89%E6%A0%91


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM