目錄
1、基本概念
遍歷二叉樹是以一定的規則將二叉樹中的結點排列成一個線性序列,從而得到幾種遍歷序列,使得該序列中的每個結點(第一個和最後一個結點除外)都有一個直接前驅和直接後繼。
傳統的二叉鏈表存儲僅能體現一種父子關係,不能直接得到結點在遍歷中的前驅或後繼。前面提到,在含n個結點的二叉樹中,有n+1個空指針。由此設想能否利用這些空指針來存放指向其前驅或後繼的指針?這樣就可以像遍歷單鏈表那樣方便地遍歷二叉樹。引入線索二叉樹正是爲了加快查找結點前驅和後繼的速度。
規定:若無左子樹。令lchild指向其前驅結點;若無右子樹,令rchild指向其後繼結點。如下表所示,還需要增加兩個標誌域標識指針域是指向左(右)孩子還是指向前驅(後繼)。
lchild | ltag | data | rtag | rchild |
其中,標誌域的含義如下:
線索二叉樹的存儲結構描述如下:
typedef struct ThreadNodes
{
ElemType data;
struct ThreadNode *lchild,*rchild;
int ltag,rtag; //左右線索標誌
}
以這種結點結構構成的二叉鏈表作爲二叉樹的存儲結構,稱爲線索鏈表,其中指向結點前驅和後繼的指針稱爲線索。加上線索的二叉樹稱爲線索二叉樹。
2、中序線索二叉樹的構造
二叉樹的線索化是將二叉鏈表中的空指針改爲指向前驅或後繼的線索。而前驅或後繼的信息只有在遍歷時才能得到,因此線索化的實質就是遍歷一次二叉樹。
以中序線索二叉樹的建立爲例。附設指針pre指向剛剛訪問過的結點,指針p指向正在訪問的結點,即pre指向p的前驅。在中序遍歷的過程中,檢查p的左指針書否爲空,若爲空就將它指向pre;檢查pre的右指針是否爲空,若爲空就將它指向p,如圖30-1所示
圖31-1 中序線索二叉樹及其二叉鏈表示
通過中序非遞歸遍歷建立中序線索二叉樹
template<typename DataType>
inline void BinaryTree<DataType>::CreateInThreadTree(ThreadNodes<DataType>* &root) //root是還未加線索的普通二叉樹
{
ThreadNodes<DataType> * p = root;
ThreadNodes<DataType> *pre = NULL;
stack<ThreadNodes<DataType> *> myStack;
myStack.push(p);
while (!myStack.empty())
{
while (p->LChild && p->ltag != 1)
{
myStack.push(p->LChild);
p = p->LChild;
}
ThreadNodes<DataType> *node = myStack.top();
if (pre)
{
if (pre->RChild)
pre->rtag = 0;
else
{
pre->RChild = node;
pre->rtag = 1;
}
if (node->LChild)
node->ltag = 0;
else
{
node->LChild = pre;
node->ltag = 1;
}
}
myStack.pop();
cout << node->data << " ";
pre = node;
if (node->RChild && node->rtag != 1)
{
myStack.push(node->RChild);
p = myStack.top();
}
}
}
3、先序線索二叉樹和後序線索二叉樹
圖31-2 先序線索二叉樹
同理,我就不用再畫後序線索二叉樹了吧。
想飛上天,和大佬肩並肩,哈哈