? 二叉树的遍历实际上是对一个非线性结构进行线性化的操作,使结点按照某个次序进行排列。以二叉链表作为存储结构时,只能找到结点的左、右孩子信息,而不能直接得到该结点在任一遍历序列中的前驱和后继信息,这种信息只有在遍历的动态过程中才能得到。这对于经常需要进行查找结点前驱或后继的访问不方便。
? 根据二叉树的特性,n个结点的二叉树,采用链式存储结构时,有n+1个空链域,可以利用这些空链域存放指向结点的直接前驱和直接后继结点的指针。为此做如下规定:当结点的左指针为空(即无左子树)时,令该指针指向按某种方式遍历二叉树时该结点的前驱结点;当结点的右指针为空(即无右子树)时,令该指针指向按某种方式遍历二叉树时该结点的后继结点。为了避免概念混淆,可定义这种指向结点的前驱或后继的指针信息为线索。
typedef struct BiThrNode
{
ElemType data;
struct BiThrNode *lchild,*rchild; //左右指针
int ltag,rtag; //左右指针类型标志,0表示指针,1表示线索
}*BiThrTree;
? 为了方便起见,依照线性表的存储结构,在二叉树的线索链表上也添加一个头结点,并令其lchild域的指针指向二叉树的根结点,其rchild域指针指向某种次序遍历时访问的最后一个结点;反之,令二叉树中序序列中第一个结点的lchild域指针和最后一个结点rchild域的指针均指向头结点。这样二叉树建立了一个双向线索链表。
? 在线索二叉树上进行遍历,只要先找到序列中的第一个结点,然后依次找结点后继直至其后继为空时为止。例如,在中序线索二叉树中寻找某个结点的后继结点有以下两种情况:
(1)若结点的右标志位1,则其右指针指向的结点就是后继。
(2)若结点的右标志位0,则其右指针指向右子树,根据中序遍历的定义,沿着右子树寻找最左的结点,即顺着右子树的左指针向下,直到左标志为1的结点,就是其后继结点。
由此可以给出中序线索二叉树的遍历算法。?
void InOrderTraverse_Thr(BiThrTree root)
{
p=root->lchild; //p指向二叉树的根结点
while(p!=root) //若不为空树
{
while(p->ltag==0)
p=p->lchild;
printf("%c",p->data); //访问其左子树为空的结点
while(p->rtag==1&&p->rchild!=root) //访问右线索所指后继结点
{
p=p->rchild;
printf("c",p->data);
}
p=p->rchild; //进入右子树
}
}
? 对于先序线索二叉树和中序线索二叉树,可以不用栈来实现二叉树的遍历,而对于后序线索二叉树,在进行后续遍历时仍需要栈。可分为三种情况:
(1)若结点x是二叉树的根,则后继为空。
(2)若结点x是双亲的右孩子或是其双亲的左孩子且其双亲没有右子树,则其后继结点为双亲结点。
(3)若结点x是其双亲的左孩子,且其双亲有右子树,则其后继为双亲的右子树上按后序遍历列出的第一个结点。?
|