设计线索二叉树存储结构(编写二叉树代码)
中序线索二叉树
以中序二叉树为例,中序二叉树声明节点的结构体如下
typedef struct BTNode
{
int data;
BTNode* lChild;
BTNode* rChild;
}BTNode;
中序线索二叉树需要两类指针,指向左孩子、右孩子的指针,指向前驱和后继的指针,我们需要区分开它们,所以我们需要一个这样的结构体:
typedef struct TBTNode
{
int data;
int ltag;
int rtag;
TBTNode* lChild;
TBTNode* rChild;
}TBTNode;
1)如果ltag=0;则lchild为指针,指向左孩子,如果ltag=1,则lchild为线索,指向节点的直接前驱。
2)如果rtag=0;则rchild为指针,指向右孩子,如果rtag=1,则rchild为线索,指向节点的直接后继。
编写代码
思考:显然,我们需要寻找节点的空指针,lchild的指向前驱,rchild指向后继。显然,我们需要在lchild不空的时候且前驱节点不空的时候,将其指向前驱,后继同上。
if(p->lchild==NULL)
{
p-lchild = pre;
p-ltag = 1;
}
if(pre != NULL&& pre->rchlid == NULL)
{
pre->rchild = p;
pre->rtag = 1;
}
pre = p;
我们设置了一个pre作为当前节点p的跟班,这样,p为pre的后继节点,而pre为p的前驱节点,且当当前节点p线索化结束后会有一个pre = p,然后遍历到下一个节点,保证pre跟上p节点。
那么,我们单个的节点线索化完成了,就要考虑如何将其应用到整棵二叉树,我们想到二叉树的遍历算法。配合遍历,我们就可以挨个节点遍历寻找拥有空指针的节点。
那么中序线索二叉树自然就需要配合中序遍历来线索化,
遍历左子树
线索化
遍历右子树
于是就有下面的代码:
if(p != NULL)
{
inThread(p->lchid,pre);
if(p->lchild==NULL)
{
p-lchild = pre;
p-ltag = 1;
}
if(pre != NULL&& pre->rchlid == NULL)
{
pre->rchild = p;
pre->rtag = 1;
}
pre = p;
inThread(p->rchild,pre);
}
遍历中序线索二叉树
我们现在尝试遍历中序线索二叉树。首先我们需要找到起始节点,中序遍历第一个节点一定是最左下节点(不一定是叶节点)我们可以得到下面代码:
TBTNode *First(TBTNode *p)
{
while(p->ltag==0)
p=p->lchild;
return p;
}
至于为什么找到第一个节点时,ltag会变为1可以看线索化的代码,此时p->lchild为NULL,将pre赋给p-lchild而ltag置为1.
找到第一个点自然要开始遍历了,中序遍历中找一个后继节点,要么是右子树的最左下,要么就是需要逐步退出遍历回到上层。那么中序线索二叉树找一个节点的下一个节点就有两种可能:右子树的最左下、p->rchild(线索,直接指向上层的某一个节点)。
TBTNode *Next(TBTNode *p)
{
if(p->rtag==0)
return First(p->rchild);
else
return p->rchild;
}
那么如何找前驱和最后一个节点呢?很简单,将ltag和rtag、lchild和rchild互改,再改一下函数名,就得到寻找最后节点和前驱节点的代码啦。(这里应该有一点问题,互改之后的找最后一个节点的判断语句while(p->ltag==0)应该起不到作用,因为最后一个节点即使rchild是空的但是已经逐步退出递归结束程序了,rtag没有被赋值)