线索二叉树
遍历二叉树是以一定规则将二叉树中结点排列成一个线性序列,得到二叉树中结点的先序序列或中序序列或后序序列。这实际上是对一个非线性序列结构进行线性化操作,使每个结点(除了第一和最后一个)在这些线性序列中有且仅有一个直接前驱和直接后继。
现在的问题是,在常用的二叉链表作为存储结构时,我们只能找到结点左右孩子的信息,而不能直接找到结点在任意序列中前驱以及后继的信息,这些信息只能通过遍历的动态过程中得到。
另一方面,在一个含有n个结点普通二叉树当中,有n+1个空链域,我们利用这些空链域来存储结点之间的前驱后继关系。在原有二叉树结点结构的基础上增加两个标志域,来判断一个结点的左右孩子指针到底指向的真的是左右子树还是前驱后继结点。
以下是线索二叉树结点的代码定义:
typedef struct BiTNode {
int data;
struct BiTNode* lchild;
struct BiTNode* rchild;
int ltag, rtag;//左右线索标志
}BiTNode,* BiTree;
BiTNode* pre = NULL;//全局变量,指向当前访问结点的前驱
线索二叉树的建立
下来,我们线索化一个二叉树。分以下三个步骤,首先建立线索:
void BuildThread(BiTree T) {
//建立线索
if (T->lchild == NULL) {
//左子树为空,建立前驱结点
T->lchild = pre;
T->ltag = 1;
}
if (pre != NULL && pre->rchild == NULL) {
//右子树为空,建立后继结点
pre->rchild = T;
pre->rtag = 1;
}
pre = T;
}
第二步,采用中序遍历的思想,遍历到每一个结点,对每一个结点建立线索,这样的建立方式叫中序线索化:
void ThreadBiTree(BiTree T) {
if (T != NULL) {
ThreadBiTree(T->lchild);
BuildThread(T);
ThreadBiTree(T->rchild);
}
}
最后,整理之前两块代码,我们建立一个线索二叉树:
void BuildBiTree_Thr(BiTree T) {
if (T != NULL) {
ThreadBiTree(T);
if (pre->rchild == NULL)
pre->rtag = 1;
}
printf("\n线索二叉树建立完成!\n");
}
采用中序遍历线索化二叉树的方式时,需注意,最后被访问的结点无法通过第二步的递归调用被线索化。从而在第三步对最后一个被访问的结点单独进行处理。
线索二叉树的中序遍历
由于我们采用中序线索化的方式线索化了二叉树,所以在遍历的时候也应该采用中序遍历线索二叉树的方式。通过这个操作,我们可以得到线索二叉树的中序序列。
首先要如何知道一个结点P的后继位置。两种情况,直接给出结论:
1.如果P的右标志域是1,即说明P的右指针是一个线索,那么P的后继就是P的右指针所指向的结点。
2.如果P的右标志域是0,说明P这个结点有右子树,那么P的后继结点应该是P的右子树当中最左下的结点。
代码实现如下:
BiTNode* FirstNode(BiTNode* p) {
//寻找最先被访问的结点
while (p->ltag