如果所用的二叉树需经常遍历或查找结点时需要某种遍历序列中的前驱和后继,那么采用线索二叉链表的存储结构是非常不错的选择。
因为以二叉链表作为存储结构时, 只能找到结点左、右孩子的信息, 而不能直接得到结点在任一序列中的前驱和后继信息, 这种信息
只有在遍历的动态过程中才能得到。
那么如何保存这种在遍历过程中得到的信息呢? 我们可以先分析一下: 对于一个有n个结点的二叉链表, 每个结点有指向左右孩子的两个指针域,
所以一共是2n个指针域。而n个结点的二叉树一共有n-1条分支线数,也就是说,其实是存在2n - (n-1) = n+1个空指针域。由此设想, 能否
利用这些空域来存放结点的前驱和后继信息。
试作规定如下:
如结点有左子树,则其lchild域指示其左孩子,否则令lchild域指示其前驱;如结点有右子树,则其lchild域指示其右孩子,否则令lchild域指示其后继。
这引出一个问题:如何知道某一结点的lchild是指向其左子树还是前驱, rchild指向右子树还是后继? 因此需要再增加两个标志域Ltag和Rtag。
Ltag为0时指向改结点的左子树,为1时指向该结点的前驱;
Rtag为0时指向改结点的右子树,为1时指向该结点的后继;
/**
* 二叉树的二叉线索存储结构定义
*
*/
typedef enum PointerTag {Link,Thread}; /** Link==0表示指向左右孩子指针,Thread==1 表示指向前驱或后继的线索*/
typedef struct BiThrNode {
TElemType data;
struct BiThrNode *lchild, *rchild; // 左右孩子指针
PointerTag Ltag;
PointerTag Rtag; /** 左右标志 */
}BiThrNode, *BiThrTree;
/** 中序遍历进行中序线索化*/
BiThrTree pre; // 全局变量, 始终指向刚刚访问过的结点
void InThreading(BiThrTree p)
{
if (p)
{
InThreading(p->lchild); // 递归左子树线索化
if (!p->lchild) // 没有左子树
{
p->Ltag = Thread;
p->lchild = pre; // 左孩子指针指向前驱
}
if (!pre->rchild) // 这里, 因为还没有访问到p结点的后继,因此只能对它的前驱结点pre的右指针rchild做判断,
{ // if (!pre->rchild) 表示如果为空, 则p就是pre的后继, 于是pre->rchild=p
pre->Rtag = Thread;
pre->rchild = p;
}
pre = p; // 保持pre指向p的前驱
InThread(p->rchild);
}
}
/** 线索化完成后,就好比为二叉树建立了一个双向线索链表,
* 下面开始进行遍历
*
*/
Status InOrdertraverse_Thr(BiThrTree T)
{
BiThrTree p;
p = T->lchild; // p指向根结点
while (p != T) // 空树或遍历结束时, p == T
{
while (p->Ltag == Link)
p = p->lchild;
printf("%c", p->data); // 访问结点
while (p->Rtag == Thread && p->rchild != T)
{
p = p->rchild;
printf("%c", p->data); // 访问后继结点
}
p = p->rchild;
}
return OK;
}