//充分利用空链域来存放结点的前驱和后继信息
typedef struct BiThrNode
{
TElemType data;
struct BiThrNode *lchild, *rchild;
int LTag, RTag;
}
/*
lchild LTag data RTag rchild
*/
/*
LTag=0,lchild指示结点的左孩子
LTag=1,rchild指示结点的前驱
RTag=0,lchild指示结点的左孩子
RTag=1,rchild指示结点的后继
*/
//中序遍历线索化
//线索化只是针对有空链域的结点,只有这样的结点才会有前驱或者后继或者二者都有
//中序遍历一定是先递归左子树,再操作,再递归右子树
void InThreading(BiThrTree p)
{
if(p)
{
InThreading(p->lchild);
//如果p的左孩子为空
//因为是中序遍历(左中右),说明p有前驱
if(!p->lchild)
{
p->LTag = 1;
p->lchild = pre;
}
else //因为是递归,所以会遇到有左孩子的结点,设置LTag为0
{
p->LTag = 0;
}
//如果p没有右孩子,那么p的右指针原先就会为空,线索化以后,p的右指针将会指向后继
//但因为是中序遍历,递归完左子树后,才回到p,所以无法判断p的右孩子
//那么递归完左子树后,pre就是左子树中序遍历的最后一个结点
//如果p的前驱没有右孩子
//那么前驱就有后继
if(!pre->rchild)
{
pre->RTag = 1;
pre->rchild = p;
}
else
{
pre->Rtag = 0;
}
//对于右子树来说,p就是右子树最开始的前驱
pre = p;
InThreading(p->rchild);
}
}
//带头结点的二叉树中序线索化
void InOrderThreading(BiThrTree *Thrt, BiThrTree T)
{
//建立头结点,头结点有左孩子,无右孩子
//那么对于头结点来说,如果T非空,那么左孩子就为T
//右孩子初始化为头结点,即自己是自己的后继
*Thrt = new BiThrNode;
(*Thrt)->LTag = 0;
(*Thrt)->RTag = 1;
(*Thrt)->rchild = *Thrt;
if(!T)
{
//如果T为空树,那么头结点的前驱也指向自己,即自己是自己的左孩子
(*Thrt)->lchild = Thrt;
}
else
{
//如果T非空,那么左孩子就为T
(*Thrt)->lchild = T;
//pre作为整个T的前驱
pre = Thrt;
InThreading(T);
//中序线索化完以后,就到了中序遍历的最后一个结点
//在对最后一个结点线索化可以确定最后一个结点一定没有右孩子,所以在pre赋值给p(即最后一个结点)后,不会进行右子树递归
pre->rchild = *Thrt;
pre->RTag = 1;
*Thrt->rchild = pre;
//头结点的后继是最后一个结点,最后一个节点的后继是头结点 = =
}
}
//遍历中序线索二叉树,这里的T是带了头结点的
void InOrderTraverse_Thr(BiThrTree T)
{
//带头结点的线索二叉树的左孩子指向真正的树
p = T->lchild;
while(p != T)
{
//沿左孩子向下,即找到中序遍历的第一个结点
while(p->Ltag == 0)
{
p = p->lchild;
}
//访问其左子树为空的结点
cout<<p->data;
//沿线索访问后继结点,即中间结点
while(p->RTag == 1 && p->rchild != T)
{
p = p->rchild;
cout<<p->data;
}
//所以转向p的右子树,对右子树线索化遍历
p = p->rchild;
}
}
数据结构总结 第五章 二叉树——线索二叉树
最新推荐文章于 2022-11-19 17:53:05 发布