資料結構與演算法11-線索二叉樹
線索二叉樹
線索二叉樹的原理
我們把指向前驅和後繼的指標稱為線索,加上線索的二叉連結串列稱為線索,加上線索的二叉連結串列為線索連結串列,相應的二叉樹就稱為線索二叉樹(Threaded Binary Tree)
lchild |
ltag |
data |
rtag |
rchild |
其中:
l ltag為0時指向該結點的左孩子,為1時指向該結點的前驅
l rtag為0時指向該結點的右孩子,為1時指向該結點的後繼
實現程式碼如下:
typedef enum{Link,Thread} PointerTag; typedef struct BiThrNode { TElemType data; struct BiThrNode *lchild,*rchild; PointerTag LTag; PointerTag RTage; }
線索化的實質就是將二叉連結串列中的空指標改為指向前驅或後繼的線索。由於前驅和後繼資訊只有在遍歷該二叉樹時才能得到,所以線索化的過程就是在遍歷的過程中修改空指標的過程。
/*中序遍歷進行中序線索化*/ BiThrTree pre; //全域性變數,始終指向剛剛訪問過的結點 void InThreading(BiThrTree p) { if(p) { InThreading(p->lchild); //遞迴左子樹線索化 if(!p->lchild) { p->LTag=Thread p->lchild = pre; } if(!pre->rchild) { pre->RTag=Thread; pre->rchild=p; } pre = p; //保持pre指向p的前驅 InThreading(p->rchild); //遞迴右子樹線索化 } }
除紅色部分和二叉公示的中序遍歷遞迴程式碼幾乎完全一樣。只不過將本是列印的結點的功能改成線索化的功能。
中間加粗的程式碼做了這樣一些事情if(!p->lchild)表示如果某結點的左指標域為空,因為其前驅結點剛剛訪問過,賦值給了pre,所以可以將pre賦值給p->lchild,並修改p-Ltag=Thread(也就是定義為1)完成前驅結點的線索化。
後繼就是要稍稍麻煩一些。因為此時p結點的後繼還沒有訪問,因此只能對它前驅結點pre的右指標rchild做判斷,if(!pre->rchild)表示如果為空,並且設定pre->RTag=Thread,完成後繼結點的線索化。完成前驅和後繼的判斷後,別忘記將當前的結點p賦值給pre,以便於下一次使用。
有了線索二叉樹後,我們對它的進行遍歷時發現,其實就等於是操作一個雙向連結串列結構。
和雙向連結串列結構一樣,在二叉樹線索連結串列上新增一個頭結點,並令其lchild域的指標指向二叉樹的根結點,其rchild域指標指向之前中序遍歷時訪問的最後一個結點。反之,令二叉樹的中序遍歷中的第一個結點中,lchild域指標和最後一個結點的rchild域均指向頭結點。這樣定義的好處就是我們既可以從第一個結點起順序後繼進行遍歷,也可以從最後一個結點起順序前驅進行遍歷。
遍歷程式碼如下:
T指向頭結點,頭結點左鏈lchild指向根結點,頭結點右鍵rchild指向中高序遍歷的最後一個結點,中序遍歷二叉線索連結串列表示的二叉樹T
Status InOrderTraverse_Thr(SiThrTree T)
{
BiThrTree p;
p = T->lchild;
while(p!=T)
{
while(p->LTag==Link)
p=p->lchild;
printf(“%C”,p->data);
while(p->RTag==Thread &&p->rchild!=T)
{
p=p->rchild;
printf(“%C”,p->data);
}
p=p->rchild;
}
}