线索二叉树
顺便说一下遍历二叉树的方法(递归,用栈,线索二叉树,还有二叉排序树)
提出问题:
线索二叉树的定义和结构,通过遍历二叉树可得到节点的一个线性序列,在线性序列中,很难容求得某个节点的直接前驱和后继,,但是在二叉树上只能找到节点的左孩子,右孩子,节点的前驱和后继只有在遍历过程中才能得到,那末,如何保存遍历二叉树后动态得到的线性序列以便快速找到某个节点的直接前驱和后继?
规定:若结点有左孩子,则lchild指示其左孩子,否则,lchild 指示其前驱,若结点有右孩子,则rchild指示其右孩子,否则,rchild域指示其后继,为了表示lchile和rchild域指向的是左右孩子还是前驱,可以加两个标志域,以明确lchild和rchild的指向
节点结构:
lchild |
ltag |
data |
rtag |
rchild |
若节点右左子树,则左链域lchild指示其左孩子(ltag=0),否则,令左链域指示其前驱(ltag=1);
若结点有右子树,则右链域rchild指示其右孩子(rtag=0),否则,令右链域指示其后继(rtag=1);
线索二叉树的类型定义:
typedef struct BiThrNode
{ ElemTypedata ;
struct BiThrNode* lchild, * rchild;
int ltag, rtag;
}BiThrNode, * BiThrTree;
其中: ltag= 0 lchild域指示结点的左孩子
ltag= l child域指示结点的前驱
rtag= 0 rchild域指示结点的右孩子
rtag= 1 rchild域指示结点的后继
以这种节点结构构成的二叉链表作为二叉树的存储结构,叫做线索链表,其中指向前驱和后继的指针叫做线索,加上线索的二叉树叫做线索二叉树,对二叉树以某种次序遍历使其变为线索二叉树的过程叫做线索化。
二叉树的线索化:
基本思想:二叉树的线索化实质上是遍历一颗二叉树,访问节点的操作是检查此节点的左右指针域是否为空,如果为空,将他改为指向其前驱或后继节点的线索。
以中序线索化为例说明其线索化过程:
(简单一点来说就是中序遍历里面的打印语句的替换,替换成什莫呢?就要看当前节点的lleft是否为空,为空的化就让他指向pre,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,如果上一个节点的右指针域为空,那末就让他指向当前节点p),,,(不好理解的化就自己中序遍历一颗二叉树画一画)
为实现这一过程,设指针p指向当前结点,pre始终指向刚刚访问过的结点,即p的前驱,以便于修改pre的后继线索和p的前驱线索。在线索化算法中访问当前结点p的处理方法如下:
①若结点p的左指针域为空,则将其标志位置为1,并使p->lchild指向中序前驱结点pre(即左线索化);
②若结点pre的右指针域为空,则将其标志位置为1,并使pre->rchild指向中序后继结点p(即右线索化);
③将pre指向刚刚访问过的结点p(即pre=p),线索化p的右子树。
算法如下:
BiThrTree pre ; /* 全局变量pre始终指向刚访问的结点*/
int InorderThread( BiThrTree * head , BiThrTree bt)
{ *head = (BiThrTree)malloc(siazeof( BiThrNode)) ;
if ( * head == NULL ) return 0;/* 线索化时头结点空间分配失败,返回 */
( * head ) -> ltag= 0 ; ( * head ) -> rtag= 1;
( * head ) -> rchild= * head ; /* 右指针回指 */
if ( bt== NULL ) ( * head ) -> lchild= * head ;/* 空二叉树左指针回指 */
else
{ ( * head ) -> lchild= bt; /*头结的左孩子指针指向二叉树的根结点*/
pre = * head ;
InThreading( bt) ; /* 中序遍历二叉树并线索化 */
pre -> rchild= * head ;
pre -> rtag= 1 ; /* 最后一个结点线索化 */
( * head ) -> rchild= pre ;
}
return 1 ;
}
void InThreading( BiThrTree p )
{ if ( p )
{ InThreading( p-> lchild) ;
if(p->lchild==NULL) /* p无左孩子,左指针域为线索 */
{ p->ltag=1 ; p–>lchild=pre ; }
if (pre->rchild==NULL) /*pre无右孩子,其右指针域为线索*/
{ pre->rtag=1; pre->rchild=p; }
pre = p ;
InThreading( p -> rchild) ;
}
}
中序线索二叉树查找结点的前驱节点算法如下:
BiThrTree InPreNode(BiThrTree p)
{ BiThrTree pre;
pre = p->lchild;
if(p->ltag!= 1) /* 结点p有左孩子*/
while(pre->rtag== 0) pre = pre->rchild;
/* 从左子树的根结点开始,沿右指针域往下查找,直到没有右孩子为止 */
return pre; /* 返回结点p的前驱结点*/
}
中序线索二叉树查找结点的后继节点算法如下:
BiThrTree InPostNode(BiThrTree p)
{ BiThrTree post;
post = p->rchild;
if(p->rtag!= 1) /* 结点p有右孩子*/
while(post->ltag== 0) post = post->lchild;
/* 从右子树的根结点开始,沿左指针域往下查找,直到没有左孩子为止 */
return post; /* 返回结点p的后继结点*/
}
相关阅读
为什么要有线索二叉树? 为了解决无法直接找到该结点在某种遍历序列中的前驱和后继结点的问题 但是同时出现了二叉链表找左、右孩子