不知道你是否和我當時一樣,對於線索二叉樹,有點雲里霧里的感覺,現在我們來一起探討下吧。
首先,我們所應該知道的是:線索二叉樹是對二叉鏈表中空指針的充分利用,也就是說,使得原本是空指針的轉化成在某種遍歷的順序下,指向該結點的前驅和后繼。也許聽的有點糊塗,沒關系,請接着往下看。
在二叉鏈表中,每個結點都帶有*leftChild和*rightChild,兩個指針,而除根結點外,每個結點只被一個指針所對應,要么是leftChild,要么是rightChild.而總共有2*n個指針,也就是說,有2*n-(n-1)個空指針,從這個角度,也說明了線索二叉樹的必要性。
線索二叉樹在二叉鏈表的基礎上增加了兩個成員數據:leftTag,rightTag;用來標記當前結點的leftChild,rightChild指針指向的是孩子,還是線索。leftTag=rightTag=1,表示線索,leftTag=rightTag=0;表示孩子;
下面給出中序遍歷順序下的線索二叉樹的構造函數,*p為根;
template <class ElemType>
void InThreadBinTree<ElemType>::InThreadHelp(ThreadBinTreeNode<ElemType> *p,ThreadBinTreeNode<ElemType> *pre)
{
if(p!=NULL)
{
InThreadHelp(p->leftChild,pre);
if(p->leftChild==NULL)
{
p->leftChild=pre;
p->leftTag=1;
}
else
p->leftTag=0;
if(pre!=NULL&&pre->rightChild==NULL)
{
pre->rightChild=p;
pre->rightTag=1;
}
else if(pre!=NULL)
{
pre->rightTag=0;
}
pre=p;
InThreadHelp(p->rightChild,pre);
}
}
該段程序可被分為3部分,1.左孩子的線索化,2.p的線索化,3.p右孩子的線索化。
在中序遍歷的前提下,判斷p是否有空指針,然而,再遍歷右子樹之前,如果p->rightChild=NULL是無法確定p的后繼的,因此,此時,只能判斷p->leftChild是否為空,若為空,則leftChild=pre;此時,判斷pre->rightChild是否為空,如為空,pre->rightChild=p;
p=pre;你也許注意到了,此時pre->rightChild,也就是先前p->rightChild,此時可以判斷了,並且后繼就是現在的p;此時所有空指針的線索化已經完成。當然,第一個結點的leftchild,和最后一個結點的rightchild是空指針,因為他們不需要線索化。
那么問題又來了,做了這么多,線索二叉樹有什么用,或者說要怎么用。
以下是個人理解;
通過構造線索二叉樹,我們可以很容易確定樹中任何一個結點在某種遍歷順序下的前驅或者后繼。不妨以中序遍歷為例;
樹中必然存在這樣兩種結點:被線索化了的,以及沒有被線索化的
被線索化的,leftTag==1,leftchild即為前驅,rightTag==1,rightChild即為后繼;
若leftTag==0,則該結點的前驅通過leftchild為根的樹的rightchild循環,直到rightTag==1,即為其前驅,
若rightTag==0,則通過rightchild為根的leftchild循環,直到leftTag==1,即為其后繼。
也就是說,任何結點按中序遍歷的順序的前驅和后繼都能找到。這就是線索二叉樹的作用,方便確定某種遍歷的順序。