无论哪一种序列线索化都不要忘记处理最后一个结点的rchild和rtag;
注意先序线索化的死循环问题,当ltag==0的时候才能对左子树先序线索化。
1、中序线索二叉树的构造
1、1一般二叉树中序遍历寻找前驱节点
//一般形式的二叉树寻找中序遍历序列的前驱结点
typedef struct BiTNode{
ElemType data;
struct BiTNode *lchild,*rchild; //左右孩子指针
}BiTNode,*BiTree;
//全局变量
BiTNode *pre=NULL; //记录当前结点的前驱结点
BiTNode *q; //题目要求的指定结点
BiTNode *final=NULL; //最终找到题目要求结点的前驱结点
void FindPre(BiTree T)
{
if(T!=NULL)
{
FindPre(T->lchild); //遍历左子树
visit(T); //访问根节点
FindPre(T->rchild); //遍历右子树
}
}
void visit(BiTNode *p)
{
if(q==p) //找到需要的结点,返回前驱
final=pre;
else //否则当前指针往后走,同时更新前驱
pre=p;
}
1.2 中序线索二叉树的构造
//线索二叉树寻找中序遍历前驱结点
typedef struct TheadNode{
ElemType data;
struct TheadNode *lchild,*rchild; //左右孩子指针
int ltag,rtag; //左右线索标志
}TheadNode,*TheadTree;
//全局变量
TheadNode *pre=NULL; //记录当前结点的前驱结点
//中序遍历二叉树,一边遍历,一边线索化
void InThead(TheadTree T)
{
if(T!=NULL)
{
InThead(T->lchild); //中序遍历左子树
visit(T); //访问根节点
InThead(T->rchild); //中序遍历右子树
}
}
void visit(TheadNode *p)
{
if(p->lchild==NULL) //当前结点的左子树为空,建立前驱线索
{
p->lchild=pre;
p->ltag=1;
}
if(pre!=NULL&&pre->rchild==NULL) //前驱结点不为空,并且前驱结点的右结点为空,建立后继线索化
{
pre->rchild=p;
pre->rtag=1;
}
pre=p; //更新前驱结点
}
//通过中序遍历建立中序遍历线索二叉树
void CreatInThread(TheadTree T)
{
pre=NULL; //初始化前驱结点
if(T!=NULL) //非空二叉树才需要线索化
{
InThead(T);
if(pre->rchild==NULL) //遍历处理最后一个结点(其实最后一个结点的右孩子一定是空)
pre->rtag=1;
}
}
2、先序线索化
//全局变量
TheadNode *pre=NULL; //记录当前结点的前驱结点
//先序遍历二叉树,一边遍历,一边线索化
void PreThead(TheadTree T)
{
if(T!=NULL)
{
visit(T); //先访问根结点
if(T->ltag==0) //由于访问根结点时可能会更改其前驱结点的指向,为了避免死循环,所以当左孩子不是其前驱线索时才访问
{
PreThead(T->lchild);
}
PreThead(T->rchild); //遍历右子树
}
}
void visit(TheadNode *p)
{
if(p->lchild==NULL) //当前结点的左子树为空,建立前驱线索
{
p->lchild=pre;
p->ltag=1;
}
if(pre!=NULL&&pre->rchild==NULL) //前驱结点不为空,并且前驱结点的右结点为空,建立后继线索化
{
pre->rchild=p;
pre->rtag=1;
}
pre=p; //更新前驱结点
}
//通过先序遍历建立先序遍历线索二叉树
void CreatPreThread(TheadTree T)
{
pre=NULL; //初始化前驱结点
if(T!=NULL) //非空二叉树才需要线索化
{
InThead(T);
if(pre->rchild==NULL) //遍历处理最后一个结点(其实最后一个结点的右孩子一定是空)
pre->rtag=1;
}
}
特别注意在先序线索化的过程中,当访问完根节点开始访问其左子树的时候,要判断其左孩子是不是前驱线索,避免出现死循环。
3、后序线索化
//全局变量
TheadNode *pre=NULL; //记录当前结点的前驱结点
//后序遍历二叉树,一边遍历,一边线索化
void PreThead(TheadTree T)
{
if(T!=NULL)
{
PreThead(T->lchild);
PreThead(T->rchild);
visit(T); //访问根结点
}
}
void visit(TheadNode *p)
{
if(p->lchild==NULL) //当前结点的左子树为空,建立前驱线索
{
p->lchild=pre;
p->ltag=1;
}
if(pre!=NULL&&pre->rchild==NULL) //前驱结点不为空,并且前驱结点的右结点为空,建立后继线索化
{
pre->rchild=p;
pre->rtag=1;
}
pre=p; //更新前驱结点
}
//通过后序遍历建立后序遍历线索二叉树
void CreatPreThread(TheadTree T)
{
pre=NULL; //初始化前驱结点
if(T!=NULL) //非空二叉树才需要线索化
{
InThead(T);
if(pre->rchild==NULL) //遍历处理最后一个结点(其实最后一个结点的右孩子一定是空)
pre->rtag=1;
}
}
4、中序线索二叉树寻找中序后继
//寻找以p为根节点的子树中,第一个被中序遍历访问的结点
TheadNode *FindFirstNode(TheadNode *p)
{
while(p->ltag==0) //第一个被访问的一定时最左下角的那个结点(但不一定是叶子结点,也可能是只有右孩子)
{
p=p->lchild;
}
return p;
}
//在中序线索二叉树中寻找p的中序后继
TheadNode *NextNode(TheadNode *p)
{
if(p->rtag==1)
return p->rchild;
else
return FindFirstNode(p->rchild);
}
对不含头结点的中序线索二叉树的遍历:
//对不含头结点的中序线索二叉树的遍历
void InOrder(TheadNode *T)
{
for(TheadNode *p=FindFirstNode(T);p!=NULL;p=NextNode(p))
{
visit(p);
}
}
5、中序线索二叉树中寻找中序前驱结点
//寻找以p为根节点的子树中,寻找左子树中最后一个被访问的结点
TheadNode *FindLastNode(TheadNode *p)
{
while(p->rtag==0) //最后一个被访问的一定时最右下角的那个结点(但不一定是叶子结点,也可能是只有左孩子)
{
p=p->rchild;
}
return p;
}
//在中序线索二叉树中寻找p的中序前驱
TheadNode *PreNode(TheadNode *p)
{
if(p->ltag==1)
return p->lchild;
else
return FindLastNode(p->lchild);
}
对不含头结点的线索二叉树逆序遍历:
//对不含头结点的中序线索二叉树的逆序遍历
void RevInOrder(TheadNode *T)
{
for(TheadNode *p=FindLastNode(T);p!=NULL;p=PreNode(p))
{
visit(p);
}
}
6、先序线索二叉树中寻找后继结点
//先序线索二叉树中寻找后继结点
TheadNode *NextNode(TheadNode *p)
{
if(p->lchild) //如果有左孩子,返回左孩子
return p->lchild;
else if(p->rchild) //没有左孩子但有右孩子,返回右孩子
return p->rchild;
else if(p->rtag==1) // 叶子结点,返回其右链域
return p->rchild;
}
7、后续线索二叉树中寻找前驱结点
//后序线索二叉树中寻找前驱结点
TheadNode *PreNode(TheadNode *p)
{
if(p->rchild) //如果有右孩子,返回右孩子
return p->rchild;
else if(p->lchild) //没有右孩子但有左孩子,返回左孩子
return p->lchild;
else if(p->ltag==1) //叶子结点,返回其左链域
return p->lchild;
}