线索二叉树
1.线索二叉树综述
引入线索二叉树是为了加快查找结点前驱和后继的速度
在有N个结点的二叉树中,有N+1个空指针,推导过程如下:
已知N0=N2+1 其中N0为度为0的结点,N2是度为2的结点
总的空指针=2N0+N1
因为:N0=N2+1
所以 总的空指针数=N0+N1+N2+1=N+1
线索化时规定:若无左子树,令lchild指向其前驱结点,若无右子树,则令rchild指向其后继节点,另许增加两个标志域表明当前指针所指对象是指向左(右)子结点还是前驱(后继)。
线索二叉树的结构体如下:
typedef struct ThreadNode{
ElemType data;
struct ThreadNode *lchild;
struct ThreadNode *rchild;
int ltag; //0表示其lchild指向左孩子,1表示指向其前驱
int rtag; //0表示rchild指向结点的右孩子,1表示指向结点的后继
}ThreadNode,*ThreadTree;
以这种结构构成的二叉树称为线索二叉树,对二叉树以某种次序遍历使其变成线索二叉树的过程称为线索化。
2.线索二叉树的构造
二叉树的线索化,实际上就是对二叉树进行一次遍历,只是在便利的时候,检查当前节点的左右指针域是否为空,若为空,将它们改为指向前驱结点或者后继节点的线索。
通过中序遍历对二叉树线索化的递归算法如下:
void InThread(ThreadTree &p,ThreadTree &pre){
if(p!=NULL){
InThread(p->lchild,pre); //pre表示中序遍历时,上一个刚刚访问的结点
if(p->lchild==NULL){
p->lchild=pre;
p->tag=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.中序线索二叉树中,中序序列下的第一个节点。
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;
}
3.中序遍历
void InOrder(ThreadNode *T){
for(ThreadNode *p=FirstNode(T);p!=NULL;p=NextNode(p))
visit(p);
}