线索二叉树
n个结点的二叉链表中含有n+1(2n-(n-1)=n+1)个空指针域。利用二叉链表中的空指针域,存放指向结点在某种遍历次序下的前趋和后继结点的指针(这种附加的指针称为"线索")。
线索二叉树数据结构
二叉树的遍历本质上是将一个复杂的非线性结构转换为线性结构,使每个结点都有了唯一前驱和后继(第一个结点无前驱,最后一个结点无后继)。对于二叉树的一个结点,查找其左右子女是方便的,其前驱后继只有在遍历中得到。为了容易找到前驱和后继,有两种方法。一是在结点结构中增加向前和向后的指针fwd和bkd,这种方法增加了存储开销,不可取;二是利用二叉树的空链指针。现将二叉树的结点结构重新定义如下:
lchild
|
ltag
|
data
|
rtag
|
rchild
|
其中:ltag=0 时lchild指向左子女;
ltag=1 时lchild指向前驱;
rtag=0 时rchild指向右子女;
rtag=1 时rchild指向后继;
构建
建立线索二叉树,或者说对二叉树线索化,实质上就是遍历一棵二叉树。在遍历过程中,访问结点的操作是检查当前的左,右指针域是否为空,将它们改为指向前驱结点或后续结点的线索。为实现这一过程,设指针pre始终指向刚刚访问的结点,即若指针p指向当前结点,则pre指向它的前驱,以便设线索。
另外,在对一颗二叉树加线索时,必须首先申请一个头结点,建立头结点与二叉树的根结点的指向关系,对二叉树线索化后,还需建立最后一个结点与头结点之间的线索。
下面是建立中序二叉树的递归算法,其中pre为全局变量。
进行中序线索化的算法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
|
voidInThreading(BiThrTree*p); //预先声明 BiThrNodeType*pre; BiThrTree*InOrderThr(BiThrTree*T) { /*中序遍历二叉树T,并将其中序线索化,pre为全局变量*/ BiThrTree*head; //线索二叉树的头结点,指向根结点 head=(BitThrNodeType*) malloc ( sizeof (BitThrNodeType)); /*设申请头结点成功*/ head->ltag=0;head->rtag=1; /*建立头结点*/ head->rchild=head; /*右指针回指*/ if (!T) head->lchild=head; /*若二叉树为空,则左指针回指*/ else { head->lchild=T;pre=head; InThreading(T); /*中序遍历进行中序线索化*/ pre->rchild=head; pre->rtag=1; /*最后一个结点线索化*/ head->rchild=pre; } returnhead; } voidInThreading(BiThrTree*p) { /*通过中序遍历进行中序线索化*/ if (p) { InThreading(p->lchild); /*左子树线索化,递归*/ if (p->lchild==NULL) /*前驱线索*/ { p->ltag=1; p->lchild=pre; } else p->ltag=0; if (p->rchild==NULL) p->rtag=1; /*后驱线索*/ else p->rtag=0; if (pre!=NULL&&pre->rtag==1) pre->rchild=p; pre=p; InThreading(p->rchild); /*右子树线索化*/ } } |
算法
线索二叉树查找前驱和后继
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
bithptr*pre=NULL; /*全程变量*/ voidINTHREAD(bithptr*p) { if (p!=NULL) { if (p->ltag==0) INTHREAD(p->lchild); /*左子树线索化*/ if (p->lchild==NULL) { p->ltag=1; p->lchild=pre; } if (p->rchild==NULL) p->rtag=1; if (pre!=NULL&&pre->rtag==1) pre->rchild=p; pre=p; /*前驱指向当前结点*/ if (p->rtag==0) INTHREAD(p->rchild); /*右子树线索化*/ } } |
(1)中序线索二叉树:若结点的ltag=1,lchild指向其前驱;否则,该结点的前驱是以该结点为根的左子树上按中序遍历的最后一个结点。若rtag=1,rchild指向其后继;否则,该结点的后继是以该结点为根的右子树上按中序遍历的第一个结点。
求后继的算法如下:
1
2
3
4
5
6
7
8
9
10
11
12
|
bithptr*INORDERNEXT(bithptr*p) { if (p->rtag==1) return (p->rchild); else { q=p->rchild; /*找右子树最先访问的结点*/ while (q->ltag==0) q=q->lchild; return (q); } } |
求前驱的算法如下:
1
2
3
4
5
6
7
8
9
10
11
12
|
bithptr*INORDERNEXT(bithptr*p) { if (p->ltag==1) return (p->lchild); else { q=p->lchild; /*找左子树最后访问的结点*/ while (q->rtag==0) q=q->rchild; return (q); } } |
(2) 后序线索二叉树:
在后序线索二叉树中查找结点*p的前驱:若结点*p无左子树,则p->lchild指向其前驱;否则,若结点*p有左子树,当其右子树为空时,其左子树的根(即p->lrchild)为其后序前驱。当其右子树非空时,其右子树的根(即p->rchild)为其后序前驱。
在后序线索二叉树中查找结点*p的后继:若结点*p为根,则无后继;若结点*p为其双亲的右孩子,则其后继为其双亲;若结点*p为其双亲的左孩子,且双亲无右子女,则其后继为其双亲;若结点*p为其双亲的左孩子,且双亲有右子女,则结点*p的后继是其双亲的右子树中按后序遍历的第一个结点。所以,求后序线索二叉树中结点的后继要知道其双亲的信息,要使用栈,所以说后序线索二叉树是不完善的。
(3)先序线索二叉树:
在先序线索二叉树中查找结点的后继较容易,而查找前驱要知道其双亲的信息,要使用栈,所以说先序线索二叉树也是不完善的。