引入线索二叉树是为了加快查找结点前驱和后继的速度
规定:若无左子树,令lchild指向其前驱结点;若无右子树,令rchild指向其后继结点。还需要增加两个标志域标识指针域,以指向左(右)孩子或前驱(后继)。
ltag=0,lchild域指示结点的左孩子
ltag=1,lchild域指示结点的前驱
rtag=0,rchild域指示结点的右孩子
rtag=1,rchild域指示结点的后继
typedef struct ThreadNode{
ElemType data; //数据元素
struct ThreadNode *lchild,*rchild; //左右孩子指针
int ltag,rtag; //左右线索标志
}ThreadNode,*ThreadTree;
1.中序线索二叉树的构造
附设指针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); //递归,线索化右子树
}//if(p!=NULL)
}
通过中序遍历建立中序线索二叉树的主过程算法如下:
void CreateInThread(ThreadTree T){
ThreadTree pre=NULL;
if(T!=NULL){ //非空二叉树,线索化
Inthread(T,pre); //线索化二叉树
pre->rchild=NULL; //处理遍历的最后一个结点
pre->rtag=1;
}
}
2.中序线索二叉树的遍历
不含头结点的线索二叉树
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)求中序线索二叉树中序序列下的最后一个结点:
ThreadNode *Lastnode(ThreadNode *p){
while(p->rtag==0)
p=p->rchild; //最右下结点(不一定是叶结点)
return p;
}
4)求中序线索二叉树中的结点p在中序序列下的前驱:
ThreadNode *Prenode(ThreadNode *p){
if(p->ltag==0)
return Nextnode(p->lchild);
else
return p->rchild; //ltag==1,直接返回前驱线索
}
5)不带头结点的中序线索二叉树的中序遍历的算法
void Inorder(ThreadNode *T){
for(ThreadNode *p=Firstnode(T);p!=NULL;p=Nextnode(p))
visit(p);
}
3.先序线索二叉树
1)先序线索二叉树的构造
void PreThread(ThreadTree p,ThreadTree &pre){
if(p!=NULL){
if(p->lchild==NULL){ //左子树为空,建立前驱线索
p->lchild=pre;
p->ltag=1;
}
if(pre!=NULL&&pre->rchild==NULL){
pre->rchild=p; //建立前驱结点的后继线索
pre->rtag=1;
}
pre=p; //标记当前结点成为刚刚访问过的结点
if(p->ltag==0)
PreThread(p->lchild,pre);
PreThread(p->rchild,pre);
}//if(p!=NULL)
}
void CreatePreThread(ThreadTree T){
ThreadTree pre=NULL;
if(T!=NULL){ //非空二叉树,线索化
PreThread(T,pre); //线索化二叉树
pre->rchild=NULL; //处理遍历的最后一个结点
pre->rtag=1;
}
}
2)在先序线索二叉树中找结点的后继
1.如果有左孩子,那左孩子就是其后继
2.如果无左孩子但有右孩子,那右孩子就是其后继
3.如果为叶结点,则右链域直接指示了结点的后继
3) 在先序线索二叉树中找结点p的前驱(需借用三叉链表)
1.如果结点p是左孩子,那么p的父结点即为其前驱
2.如果结点p是右孩子,且其左兄弟为空,那么p的父结点即为其前驱
3.如果结点p是右孩子,且其左兄弟不为空,那么p的前驱为左兄弟子树中的最后一个被先序遍历的结点
4.如果结点p是根结点,那么p没有先序前驱
4.后序线索二叉树
1)后序线索二叉树的构造
void PostThread(ThreadTree p,ThreadTree &pre){
if(p!=NULL){
PostThread(p->lchild,pre);
PostThread(p->rchild,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; //标记当前结点成为刚刚访问过的结点
}//if(p!=NULL)
}
void CreatePostThread(ThreadTree T){
ThreadTree pre=NULL;
if(T!=NULL){ //非空二叉树,线索化
PostThread(T,pre); //线索化二叉树
pre->rchild=NULL; //处理遍历的最后一个结点
pre->rtag=1;
}
}
2)在后序线索二叉树中找结点的前驱
1.如果有右孩子,那右孩子就是其前驱
2.如果无右孩子但有左孩子,那左孩子就是其前驱
3) 在后序线索二叉树中找结点p的后继(需借用三叉链表)
1.如果结点p是右孩子,那么p的父结点即为其后继
2.如果结点p是左孩子,且其右兄弟为空,那么p的父结点即为其后继
3.如果结点p是左孩子,且其右兄弟不为空,那么p的后继为右兄弟子树中的第一个被后序遍历的结点
4.如果结点p是根结点,那么p没有后序后继