目录
6.3 线索二叉树
n个结点的二叉链表结构中有多少个链域?多少个空链域?
有2n个链域,n+1个空链域;
利用这些空链域来存放某种遍历后结点的前驱和后继信息。这就是线索二叉树构成的思想;
这里的前驱和后继是指采用某种遍历(先序、中序、后序)时的前驱和后继;
采用既可以指示其前驱又可以指示后继的双链结构的二叉树被称为线索二叉树。
1.规定
a.若结点有左子树tag=0,则其lchild域指示其左孩子,否则tag=1令lchild域指示其前驱;
b.若结点有右子树,则其rchild域指示其右孩子,否则令rchild域指示其后继。
2.结点结构
线索二叉树结点类型定义:
结点类型定义:
typedef enum{Link,Thread}PointerThr;//Link==0指针,Thread==1线索
typedef struct BiThrNod
{
TElemType data;
struct BiThrNpde *lchild,*rchild;//左右指针
PointerThr ltag,rtag;//左右标志
}BiThrNode,*BiThrTree;//线索二叉树结点,*线索二叉树
3.几个概念
(1)线索链表:上述结点结构构成的二叉链表作为二叉树的存储结构,叫做线索链表;
(2)线索:指向某遍历次序的前驱或后继的指针;
(3)线索二叉树:采用线索链表表示的二叉树;
(4)线索化:对二叉树以某种次序遍历使其变为线索二叉树的过程。
4.线索二叉树以及空线索二叉树的表示方法:
设一个指针pre始终指向刚刚访问过的结点,指针p指示当前正在访问的结点。
(1)若结点p有空指针域,则将相应的标志域置1;否则0;
①若结点pre非空且右线索标志为1,
令pre->rchild指向其中序后继结点p。
②若结点p的左线索标志为1,
则令p->lchild指向其中序前趋结点pre;
(3)将pre指向刚刚访问过的结点p。
//线索化 无头结点
//定义
typedef enum{Link,Thread}PointerThr;//Link==0指针,Thread==1线索
typedef struct BiThrNod
{
TElemType data;
struct BiThrNpde *lchild,*rchild;//左右指针
PointerThr ltag,rtag;//左右标志
}BiThrNode,*BiThrTree;//线索二叉树结点,*线索二叉树
//线索化
BiThrTree pre=NULL,p=T;
void InOrderThreading(BiThrTree p)
{
if(p!=NULL)// 对以p为根的非空二叉树进行线索化
{
InOrderThreading(p->lchild); // 左子树线索化
if(p->lchild==NULL)// 建前驱线索
p->ltag=Thread;
else
p->ltag=Link;
//三目运算符
p->rtag= ( p->rchild==NULL?Thread:Link);// 建后继线索
if(pre!=NULL)
{
if(pre->rtag==Thread)
pre->rchild=p;
}
if(p->ltag==Thread)
p->lchild=pre;
pre=p; // 保持 pre 指向 p 的前驱
InOrderThreading(p->rchild); // 右子树线索化
}
}//算法结束
4.空线索二叉树的表示方法:
增加一个头结点:
Thrt->lchild=T;
Thrt->rchild是一个线索,指向线索化中最后一个结点;
Thrt->ltag=0;//左孩子指向第一个结点
Thrt->rtag=1;//右孩子指向线索化最后一个结点
带头结点的空线索二叉树的表示:
Thrt->lchild=Thrt;
Thrt->rchild=Thrt;
Thrt->ltag=0;//指针 第一个结点
Thrt->rtag=1;//线索 指向最后一个结点 此时为自身
5.二叉树的线索化:中序线索化过程:
//线索化
int InOrderThreading(BiThrTree &Thrt,BiThrTree T)
{
//头结点
Thrt=(BiThrTree)malloc(sizeof(BiThrNode));
if(!Thrt)return 0;
Thrt->ltag=Link;//左孩子指向根节点
Thrt->rchild=Thrt;//初始化先将右线索指向自己
//右孩子指向遍历序列的最后一个结点
Thrt->rtag=Thread;
if(!T)//空树
Thrt->lchild=Thrt;//前驱指向自己
else//非空树
{
Thrt->lchild=T;//左孩子指向根节点
pre=Thrt;//注意这个pre需要是全局变量
InThreading(T);
pre->rchild=Thrt;//此时的pre是最后一个结点
pre->rtag=Thread;
Thrt->rchild=pre;
}
}
void InThreading(BiThrTree p)
{
if(p)
{
InThreading(p->lchild);
if(p->lchild==NULL) // 建前驱线索
{
p->ltag=Thread;
p->lchild=pre;
}
else
{
p->ltag=Link;
}
if(pre->rchild==NULL)// 建后继线索
{
pre->rtag=Thread;
pre->rchild=p;
}
else
{
pre->rtag=Link;
}
pre=p;// 保持 pre 指向 p 的前驱
InThredaing(p->rchild);
}
} // InThreading
例1.在中序线索树中查找某结点p的中序前趋和中序后继:
中序查找前趋:
BiThrtTree Inorderpre(BiThrTree p)
{
BiThrTree pre;
if(p->ltag==1) //线索 若p没有左子树;
return p->lchild;//左孩子指向前驱
else //若p有左子树
{//中序是左根右 左子树最右边结点是这根节点的前驱
pre=p->lchild;
while(pre->rtag==0)//右孩子
pre=pre->rchild;
return pre;//
}
}
中序查找后继:
BiThrtTree Inordernext(BiThrTree p)
{
BiThrTree next;
if(p->rtag==1)//线索 无右子树
return p->rchild;//左孩子指向前驱
else//有右子树
{//中序是左根右 右子树最左边结点是这根节点的后继
next=p->rchild;
while(next->ltag==0)//右孩子
next=next->lchild;
return next;//
}
}
例2.在先序线索树中查找某结点p的先序前趋和先序后继:
先序查找后继:
BiThrtTree Preordernext(BiThrTree p)
{
//先序 根左右
if (p->LTag==0)
return (p->lchild);
else
return (p->rchild);
}
例3.在后序线索树中查找某结点p的后序前趋和后序后继:
(1)查找前趋:
BiThrtTree Postorderpre(BiThrTree p)
{
//后序 左右根
if (p->rTag==0)//有右子树
return (p->rchild);//左右跟 右子树就是前驱
else//线索 没有右子树
return (p->lchild); //线索化左孩子就是前驱
}
(2)查找后继:(不全)
1、若结点*p为根,则无后继;
2、若结点*p为其双亲的右孩子,则其后继为其双亲;
3、若结点*p为其双亲的左孩子,且双亲无右子女,则其后继为其双亲;
4、若结点*p为其双亲的左孩子,且双亲有右子女,则结点*p的后继是其双亲的右子树中按后序遍历的第一个结点。
BiThrtTree Postorderpre(BiThrTree p)
{
/*
1、若结点*p为根,则无后继;
2、若结点*p为其双亲的右孩子,则其后继为其双亲;
3、若结点*p为其双亲的左孩子,且双亲无右子女,则其后继为其双亲;
4、若结点*p为其双亲的左孩子,且双亲有右子女,则结点*p的后继是其双亲的右子树中按后序遍历的第一个结点。
*/
//后序 左右根
if(p->rtag==1)//线索 没有右子树 后继
return p->rchild;
else //应该找到上一级的双亲结点
}