概念
在二叉树的结点上加上线索的二叉树称为线索二叉树,对二叉树以某种遍历方式(如先序、中序、后序或层次等)进行遍历,使其变为线索二叉树的过程称为对二叉树进行线索化。
优点
解决了二叉树无法直接找到该结点在某种遍历序列中的前驱和后继结点的问题。
存储结构
线索二叉树中的线索能记录每个结点前驱和后继信息。为了区别线索指针和孩子指针,在每个结点中设置两个标志ltag和rtag。
当ltag=0 时lchild指向左儿子;ltag=1 时lchild指向前驱;rtag=0 时rchild指向右儿子;rtag=1 时rchild指向后继。
| lchild | ltag | data | rtag | rchild |
|---|
类型
线索二叉树可分为前序线索二叉树、中序线索二叉树和后序线索二叉树三种。
代码
结构
struct BiTreeNode
{
int LTag;
int RTag;
struct BiTreeNode *lchild;
struct BiTreeNode *rchild;
char data;
}BiTreeNode,*BiTree;
创建
int i = 0;
bool CreateBiThrTree(BiThrTree *T,char *str){
char h = str[i++];
if (h == '#') {
*T = NULL;
}else{
*T = (BiThrTree)malloc(sizeof(BiThrNode));
if (!*T) {
exit(0);
}
//生成根结点(前序)
(*T)->data = h;
//递归构造左子树
CreateBiThrTree(&(*T)->lchild,str);
//存在左孩子->将标记LTag设置为Link
if ((*T)->lchild) (*T)->LTag = 0;
//递归构造右子树
CreateBiThrTree(&(*T)->rchild,str);
//存在右孩子->将标记RTag设置为Link
if ((*T)->rchild) (*T)->RTag = 0;
}
return true;
}
在遍历过程中,访问结点的操作是检查当前的左,右指针域是否为空,将它们改为指向前驱结点或后续结点的线索。为实现这一过程,设指针 pre 始终指向刚刚访问的结点,即若指针p指向当前结点,则 pre 指向它的前驱
BiThrTree pre; /* 全局变量,始终指向刚刚访问过的结点 */
/* 中序遍历进行中序线索化*/
void InThreading(BiThrTree p){
/*
InThreading(p->lchild);
.....
InThreading(p->rchild);
*/
if (p) {
//递归左子树线索化
InThreading(p->lchild);
//无左孩子
if (!p->lchild) {
//前驱线索
p->LTag = 1;
//左孩子指针指向前驱
p->lchild = pre;
}else
{
p->LTag = 0;
}
//前驱没有右孩子
if (!pre->rchild) {
//后继线索
pre->RTag = 1;
//前驱右孩子指针指向后继(当前结点p)
pre->rchild = p;
}else
{
pre->RTag = 0;
}
//保持pre指向p的前驱
pre = p;
//递归右子树线索化
InThreading(p->rchild);
}
}
在对一颗二叉树加线索时,必须首先申请一个头结点,建立头结点与二叉树的根结点的指向关系,对二叉树线索化后,还需建立最后一个结点与头结点之间的线索
/* 中序遍历二叉树T,并将其中序线索化,Thrt指向头结点 */
bool InOrderThreading(BiThrTree *Thrt , BiThrTree T){
*Thrt=(BiThrTree)malloc(sizeof(BiThrNode));
if (! *Thrt) {
exit(0);
}
//建立头结点;
(*Thrt)->LTag = 1;
(*Thrt)->RTag = 0;
//右指针回指向
(*Thrt)->rchild = (*Thrt);
/* 若二叉树空,则左指针回指 */
if (!T) {
(*Thrt)->lchild=*Thrt;
}else{
(*Thrt)->lchild=T;
pre=(*Thrt);
//中序遍历进行中序线索化
InThreading(T);
//最后一个结点rchil 孩子
pre->rchild = *Thrt;
//最后一个结点线索化
pre->RTag = 1;
(*Thrt)->rchild = pre;
}
return true;
}