線索二叉樹 (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; /* 頭結點的右指針指向中序遍歷的最后一個結點 */ } }
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