1. 线索二叉树的基本概念
遍历二叉树是以一定的规则将二叉树中的结点排列成一个线性序列,从而得到几种遍历方法,使得该序列中的每个结点(第一个和最后一个结点除外)都有一个直接前驱和直接后继。
注意:线索二叉树是一种物理结构(或存储结构),引入二叉树的目的是为了加快查找结点前驱和后继的速度。
规定:若无左子树,令 lchild 指向其前驱结点;若无右子树,令 rchild 指向其后继结点。还需增加两个标志域标识指针域是指向左(右)孩子还是指向前驱(后继)。
线索二叉树的结点结构如下图所示:
其中,标志域的含义如下图:
线索二叉树的存储结构描述如下:
typedef struct ThreadNode {
ElemType data; //数据元素
struct ThreadNode *lchild,*rchild; //左、右孩子指针
int ltag,rtag; //左右线索标志
}ThreadNode,*ThreadTree;
按照上述线索二叉树的存储结构描述所构成的二叉链表作为二叉树的存储结构,称为线索链表,其中指向结点前驱和后继的指针称为线索。加上线索的二叉树称为线索二叉树。
标注:指向结点前驱和后继的指针称为线索。若无左子树,令 lchild 指向其前驱结点;若无右子树,令 rchild 指向其后继结点。
2. 中序线索二叉树的构造
二叉树的线索化是将二叉链表的空指针改为指向前驱和后继的线索。而前驱或后继的信息只要在遍历时才能得到,因此,线索化的实质就是遍历一次二叉树。
以中序线索二叉树的建立为例。附设指向 pre 指向刚访问的结点,指针 p 指向证在访问的结点,即 pre 指向 p 的前驱。在中序遍历过程中,检查 p 的左指针是否为空,若为空就将它指向 pre;检查 pre 的右指针是否为空,若为空就将它指向 p,中序线索二叉树及其二叉链表表示如下:
通过中序遍历对二叉树线索化的递归算法如下:
void InThread(ThreadTree &p,ThreadTree &pre){
if(p!=null){
InThread(p->lchild,pre); //递归,线索化左子树
if(p->lchild==null){ //左子树为空,建立前驱线索
p->lchild=pre;
p->ltag=1;
}
if(pre!=null&&pre->rchild==null){
pre->rchild=p; //建立前驱结点的后继线索
pre->rtag=1;
}
pre=p; //标记当前结点成为刚刚访问过的结点
InThread(p->rchild,pre); //递归,线索化右子树
}
}
通过中序遍历建立中序线索二叉树的主过程算法如下:
void CreateInThread(ThreadTree T){
ThreadTree pre=null;
if(T!=null){ //非空二叉树,线索化
InThread(T,pre); //线索化二叉树
pre->rchild=null; //处理遍历的最后一个结点
pre->rtag=1;
}
}
为了便利,可以在二叉树的线索链表上也添加一个头结点,令其 lchild 域的指针指向二叉树的根结点,其 rchild 域的指针指向中序遍历时访问的最后一个结点;令二叉树中序序列中的第一个结点的 lchild 域指针和和最后一个结点的 rchild 域指针均指向头结点。
类似于二叉树建立了一个双向链表,方便从前往后或从后往前对线索二叉树进行遍历。带头结点的中序线索二叉树如下图所示:
3. 中序线索二叉树的遍历
中序线索二叉树的结点中隐藏了线索二叉树的前驱和后继信息。在对其进行遍历时,只要先找到序列中的第一个结点,然后依次找到结点的后继,直至其后继为空。
中序线索二叉树中找结点后继的规律是:若其右标志为“1”,则右链为线索,指示其后继,否则遍历右子树中第一个访问的结点(右子树中最左下的结点)为其后继。
不包含头结点的线索二叉树的遍历算法如下。
(1)求中序线索二叉树中中序序列下的第一个结点:
ThreadNode *Firstnode(ThreadNode *p){
while(p->ltag==0)
p=p->lchild; //最左下结点(不一定是叶节点)
return p;
}
(2)求中序线索二叉树中结点 p 在中序序列下的后继:
ThreadNode *Nextnode(ThreadNode *p){
if(p->rtag==0)
return Firstnode(p->rchild);
else
return p->rchild; //rtag==1 直接返回后继线索
}
(3)不包含头结点的中序线索二叉树的中序遍历算法如下:
void Inorder(ThreadNode *T){
for(ThreadNode *p=Firstnode(T);p!=null;p=Nextnode(p))
visit(p);
}
先序线索二叉树和后序线索二叉树和中序线索二叉树类似,只需变动线索化构造的代码段与调用线索化左右子树递归函数的位置即可,这里便不在叙述。