一.线索二叉树
1.概念
-
遍历二叉树是以一定规则将二叉树中的结点排列成一个线性序列,得到二叉树中节点的先序序列、中序序列或后序序列。也就是对非线性结构进行线性化操作,使每个节点在这些线性序列中有且仅有一个前驱和后继。
-
以二叉链表作为存储结构时,只能找到结点的左、右孩子,不能直接得到结点在任一序列中的前驱和后继信息,而这些信息只有在动态的遍历过程中才能得到,为此引入线索二叉树来保存这些在动态过程中得到的有关前驱和后继的信息。
-
虽然可以在每个节点中增加两个指针域来存放遍历时得到的前驱和后继信息,但是这样会造成结构的存储密度大大降低。由于有
n个结点的二叉链表中必定存在n+1个空链域,因此充分利用这些空链域来存放节点的前驱和后继信息。
2.结构
typedef char CElemType;
/* Link==0表示指向左右孩子指针, */
/* Thread==1表示指向前驱或后继的线索 */
typedef enum {Link,Thread} PointerTag;
/* 线索二叉树存储结点结构*/
typedef struct BiThrNode{
//数据
CElemType data;
//左右孩子指针
struct BiThrNode *lchild,*rchild;
//左右标记
PointerTag LTag;
PointerTag RTag;
}BiThrNode,*BiThrTree;
以这种结点结构构成的二叉链表作为二叉树的存储结构,叫做线索链表。
其中指向结点前驱和后继的指针,叫做线索。加上线索的二叉树称之为线索二叉树。对二叉树以某种次序遍历使其变为线索二叉树的过程叫做线索化。
3. 配置
#define OK 1
#define ERROR 0
#define TRUE 1
#define FALSE 0
#define MAXSIZE 100 /* 存储空间初始分配量 */
/* Status是函数的类型,其值是函数结果状态代码,如OK等 */
typedef int Status;
typedef char CElemType;
/* 字符型以空格符为空 */
CElemType Nil='#';
#pragma mark--二叉树构造
int indexs = 1;
typedef char String[24]; /* 0号单元存放串的长度 */
String str;
Status StrAssign(String T,char *chars)
{
int i;
if(strlen(chars)>MAXSIZE)
return ERROR;
}else{
T[0]=strlen(chars);
for(i=1;i<=T[0];i++)
T[i]=*(chars+i-1);
return OK;
}
}
4.构造二叉树
按照前序输入结点的值,构造线索二叉树
Status CreateBiThrTree(BiThrTree *T){
CElemType h;
//scanf("%c",&h);
//获取字符
h = str[indexs++];
if (h == Nil) {
*T = NULL;
}else{
*T = (BiThrTree)malloc(sizeof(BiThrNode));
if (!*T) {
exit(OVERFLOW);
}
//生成根结点(前序)
(*T)->data = h;
//递归构造左子树
CreateBiThrTree(&(*T)->lchild);
//存在左孩子->将标记LTag设置为Link
if ((*T)->lchild) (*T)->LTag = Link;
//递归构造右子树
CreateBiThrTree(&(*T)->rchild);
//存在右孩子->将标记RTag设置为Link
if ((*T)->rchild) (*T)->RTag = Link;
}
return OK;
}
5.操作
5.1 中序线索化
/* 中序遍历进行中序线索化*/
void InThreading(BiThrTree p){
/*
InThreading(p->lchild);
.....
InThreading(p->rchild);
*/
if (p) {
//递归左子树线索化
InThreading(p->lchild);
//无左孩子
if (!p->lchild) {
//前驱线索
p->LTag = Thread;
//左孩子指针指向前驱
p->lchild = pre;
}else
{
p->LTag = Link;
}
//前驱没有右孩子
if (!pre->rchild) {
//后继线索
pre->RTag = Thread;
//前驱右孩子指针指向后继(当前结点p)
pre->rchild = p;
}else
{
pre->RTag = Link;
}
//保持pre指向p的前驱
pre = p;
//递归右子树线索化
InThreading(p->rchild);
}
}
5.2 中序遍历二叉树,同时进行中序线索化
Status InOrderThreading(BiThrTree *Thrt , BiThrTree T){
*Thrt=(BiThrTree)malloc(sizeof(BiThrNode));
if (! *Thrt) {
exit(OVERFLOW);
}
//建立头结点;
(*Thrt)->LTag = Link;
(*Thrt)->RTag = Thread;
//右指针回指向
(*Thrt)->rchild = (*Thrt);
/* 若二叉树空,则左指针回指 */
if (!T) {
(*Thrt)->lchild=*Thrt;
}else{
(*Thrt)->lchild=T;
pre=(*Thrt);
//中序遍历进行中序线索化
InThreading(T);
//最后一个结点rchil 孩子
pre->rchild = *Thrt;
//最后一个结点线索化
pre->RTag = Thread;
(*Thrt)->rchild = pre;
}
return OK;
}
5.3 中序遍历二叉线索树
Status InOrderTraverse_Thr(BiThrTree T){
BiThrTree p;
p=T->lchild; /* p指向根结点 */
while(p!=T)
{ /* 空树或遍历结束时,p==T */
while(p->LTag==Link)
p=p->lchild;
if(!visit(p->data)) /* 访问其左子树为空的结点 */
return ERROR;
while(p->RTag==Thread&&p->rchild!=T)
{
p=p->rchild;
visit(p->data); /* 访问后继结点 */
}
p=p->rchild;
}
return OK;
}
5.4 调用
int main(int argc, const char * argv[]) {
printf("Hello, 线索化二叉树!\n");
BiThrTree H,T;
//StrAssign(str,"ABDH#K###E##CFI###G#J##");
StrAssign(str,"ABDH##I##EJ###CF##G##");
CreateBiThrTree(&T); /* 按前序产生二叉树 */
InOrderThreading(&H,T); /* 中序遍历,并中序线索化二叉树 */
InOrderTraverse_Thr(H);
printf("\n\n");
return 0;
}
5.5 输出
H D I B J E A F C G