遍历二叉树是以一定的规则将二叉树中结点排列成一个线性序列,得到二叉树中结点的前序序列或者中序序列或者后序序列。这实际上是对二叉树进行线性化操作,使得每个结点(第一个与最后一个除外)在这些线性序列中有且仅有一个直接前驱和直接后继。
但是,当二叉树以二叉链表作为存储结构时,只能找到结点的左、右孩子信息,而不能直接得到结点在任一序列中的前序和后继信息,这种信息只有在遍历的动态过程中才能得到。我们要做的就是保留这种在遍历过程中得到的信息。
一个最简单的方法就是在每个结点 上增加两个指针域分别指示结点在任一次序遍历时得到的前驱和后继信息,但是显然,这种方法将大大降低结构的存储密度。另一方面,在n个结点的二叉链表中必定存在n+1个空链域。所以较为有效的方法是利用好这些空链域来存放结点的前驱和后继的信息。
试做如下规定:
(1)若结点有左子树,则lchild指向其左孩子,否则指示其前驱;
(2)若结点有右子树,则rchild指向其右孩子,否则指示其后继。
为了避免混淆,需要在二叉链表中的结点结构中稍作修改,增加两个 标志域:
lchild | ltag | data | rtag | rchild |
其中:
ltag=0时lchild指向结点的左孩子,ltag=1时lchild指向其前驱;
rtag=0时rchild指向结点的右孩子,rtag=1时rchild指向其后继。
以这种结点结构构成的二叉链表作为二叉树的存储结构,我们叫做线索链表,其中指向前驱和后继的指针,叫做线索。加上线索的二叉树称之为线索二叉树。下图所示是一个中序二叉树,其中实线是指针,虚线是线索。对二叉树以某种次序遍历使其变为线索二叉树的过程线索化。
可以用C++语言中的结构体类型描述线索链表中的结点:
enum flag{CHILD,THREAD}; //枚举类型,枚举常量CHILD=0,THREAD=1
template<class Type>
struct ThreadNode{ //结构体类型描述线索链表的结点
Type data;
ThreadNode<Type> *lchild, *rchild;
flag ltag, rtag;
};
由于二叉树的遍历次序有4中,故有4种意义下的前驱和后继,相应的有4种线索链表:前序线索链表、中序线索链表、后序线索链表和层序线索链表。这里的讨论以中序线索链表为例。
一、创建二叉树
构造函数的功能是建立一个中序线索链表,实质上就是讲二叉链表中的空指针改为指向前驱或者后继的线索,而前驱和后继的信息只有在遍历的时候才能得到。因此建立线索链表首先要建立二叉链表,然后在遍历的过程中修改指针。建立如下:
template<class Type>
ThreadNode<Type>* InOrderThreadBiTree<Type>::Create(ThreadNode<Type> *bt){
Type ch;
cin >> ch;
if (ch == '#')bt = NULL; //建立一棵空树
else{
bt = new ThreadNode<Type>; //生成一个结点
bt->data = ch;
bt->ltag = CHILD;
bt->rtag = CHILD; //左右标志均置为0
bt->lchild = Create(bt->lchild);
bt->rchild = Create(bt->rchild); //递归建立左右子树
}
return bt;
}
在遍历过程中,访问当前结点bt的操作为: