为什么要线索化二叉树?
:传统的二叉树的链式存储,只能体现一种父子关系,不能直接得到结点在遍历中的前驱结点和后继结点,而且一般仅能从最上方往下找,很不方便,不能从任意结点开始遍历寻找前驱后继。前文中提过二叉树链式存储有n+1个空链域,所以可以充分利用这n+1个空指针构造出线索二叉树使得我们更方便的遍历二叉树。
//不线索化找前驱
typedef struct BTNode
{
int data;
struct BTNode *lchild,*rchild;
}BTNode,*BTree;
//查找结点p的前驱
BTree *p;//p指向目标结点
BTree *pre=nullptr;//前驱结点
BTree *final=nullptr;//记录最终的结点
void visit(BTree *q)
{
if (q==p)
{
final=pre;//找到p的前驱结点
}
else
{
pre=q; //pre指向当前访问的结点
}
}
void FindPre(BTree T)
{
if (T)
{
FindPre(T->lchild);
visit(&T);
FindPre(T->rchild);
}
}
//线索二叉树:
typedef struct ThreadNode
{
int data;
struct ThreadNode *lchild,*rchild;
int ltag,rtag;
}ThreadNode,*ThreadTree;
ThreadNode *pr=nullptr;//全局变量:前驱,之后三种线索化公用。
void visit(ThreadNode *T)
{
if (T->lchild==nullptr)
{
T->lchild=pr;
T->ltag=1;
}
if (pr&&T->rchild==nullptr)
{
pr->rchild=T;//建立前驱结点的后继指针
pr->ltag=1;
}
pr=T;
//最后特别处理pr,因为pr遍历到最后一个结点时,
//若右节点为空,则pr的tag应为1,所以可以利用pr是全局变量在别的函数进行判断修改
}
//中序遍历线索化:
void InThread(ThreadTree T)
{
if (T)
{
InThread(T->lchild);
visit(T);
InThread(T->rchild);
}
}
void CreateInThread(ThreadTree T)
{
pr=nullptr;//pr初始为NULL
if (T)//非空二叉树才能线索化
{
InThread(T);
if (pr->rchild==nullptr)
{
pr->rtag=1;//处理遍历的最后一个结点的tag值
}
}
}
//先序线索化:
void PreThread(ThreadTree T)
{
if (T)
{
visit(T);//先处理根节点
if(T->ltag==0)//lchild不是前驱线索,防止死循环
PreThread(T->lchild);
PreThread(T->rchild);
}
}
void CreatePreThread(ThreadTree T)
{
pr=nullptr;
if (T)
{
PreThread(T);
if (pr->rchild==nullptr)
{
pr->rtag=1;
}
}
}
//后序线索化:
void PostThread(ThreadTree T)
{
if (T)
{
PostThread(T->lchild);
PostThread(T->rchild);
visit(T);
}
}
void CreatePostThread(ThreadTree T)
{
pr=nullptr;
if (T)
{
PostThread(T);
if (pr->rchild==nullptr)//处理最后一个结点
{
pr->rtag=1;
}
}
}