线索二叉树小结

/*
Name: 线索二叉树
Copyright:
Author: 巧若拙 
Date: 30-09-14 22:13
Description: 
    遍历二叉树是以一定规则将二叉树中结点排列成一个线性序列,得到二叉树中结点的先序,中序或后序序列。
这实际上是对一个非线性结构进行线性化操作,使每个结点(除第一个和最后一个外)在这些线性序列中有且仅有一个直接前驱和直接后继。
但是,当以二叉链表作为存储结构时,只能找到结点的左,右孩子的信息,而不能直接得到结点在任一序列中的前驱和后继信息,这种信息只能在遍历的动态过程中才能得到。
    因为在有n个结点的二叉链表中必定存在n+1个空链域,故可以利用这些空链域来存放结点的前驱和后继信息。
    试做如下规定:若结点有左子树,则其lchild域指示其左孩子,否则令lchild域指示其前驱;
若结点有右子树,则其rchild域指示其右孩子,否则令rchild域指示其后继。为了避免混淆,需要改变结点结构,增加两个标志域:LTag,RTag。
    其中:LTag = 0,lchild域指示其左孩子;  LTag = 1,lchild域指示其前驱。
          RTag = 0,rchild域指示其右孩子;  RTag = 1,rchild域指示其后继。
    以这种结点组成的二叉链表作为二叉树的存储结构,叫做线索链表,其中指向结点前驱和后继的指针,叫做线索。
加上线索的二叉树叫做线索二叉树。对二叉树以某种次序遍历使其变成线索二叉树的过程叫做线索化。
    在线索二叉树上进行遍历,只要先找到序列中的第一个结点,然后依次找结点后继直到其后继为空为止;当然也可以找结点的前驱,进行逆序遍历。 
    求各种次序线索树非叶子结点的前驱和后继的方法:
前序线索树非叶子结点p的前驱:p的双亲结点;
前序线索树非叶子结点p的后继:若p有左孩子,则后继为其左孩子,否则后继是其右孩子。
后序线索树非叶子结点p的前驱:若p有右孩子,则后继为其右孩子;否则后继是其左孩子。
后序线索树非叶子结点p的后继:若p为根结点,则其后继为空;若p为其双亲结点的右孩子,或p为左孩子且其双亲结点无右孩子,则p的后继为其双亲结点;
若p为左孩子,且其双亲结点有右孩子,则其后继为双亲结点右子树的第一个结点(最左边结点)。 
中序线索树非叶子结点p的前驱:左子树的最后一个结点(最右边的结点)。
中序线索树非叶子结点p的后继:右子树的第一个结点(最左边结点)。 
前序线索树的后继很好找,但前驱是双亲结点,不便查找;虽然中序线索树前驱和后继都不是直接指向,但不需要求双亲,查找还是比较方便的;
后序线索树的前驱很好找,但是后继需要分类讨论,情况复杂,而且需要求双亲。综合考虑,中序线索树是最佳的选择。
为了方便起见,我们仿照线性表的存储结构,在二叉树的线索链表上也添加一个头结点,并令其lchild域的指针指向二叉树的根结点,
其rchild域的指针指向中序遍历时访问的最后一个结点;反之,令二叉树中序序列的第一个结点的lchild域的指针和最后一个结点的rchild域的指针均指向头结点。
这好比为二叉树建立了一个双向线索链表,既可以从第一个结点起顺后继进行遍历,也可以从最后一个结点起顺前驱进行遍历。
在应用示例中,首先生成一棵二叉排序树(输入单个字符,以#结束),并以递归方式遍历输出结点;然后把该二叉排序树中序线索化,
最后顺序和逆序各遍历一次中序线索树,其中顺序遍历中序线索树采用了4种不同的写法。 
*/


#include<stdio.h>
#include<stdlib.h>
#include<malloc.h>
#include<math.h>
#include<time.h>


#define MAXSIZE 10
#define OK 1
#define ERROR 0
#define TRUE 1
#define FALSE 0 
#define Link 0     //Link:指针
#define Thread 1   //Thread:线索


typedef char ElemType;
typedef int Status; //函数类型,其值是函数结果状态代码,如OK等 


typedef struct BiThrNode{
ElemType data;
    struct BiThrNode *lchild, *rchild;//左,右孩子指针
    int LTag, RTag; //左,右标志
} BiThrNode, *BiThrTree;


BiThrTree Successor(BiThrTree p);//返回结点p的后继
BiThrTree Precursor(BiThrTree p);//返回结点p的前驱 
void InOrderTraverse_Thr_1(BiThrTree T);//中序遍历线索二叉树, T指向头结点
void InOrderTraverse_Thr_2(BiThrTree T);//中序遍历线索二叉树, T指向头结点
void InOrderTraverse_Thr_3(BiThrTree T);//中序遍历线索二叉树, T指向头结点
void InOrderTraverse_Thr_4(BiThrTree T);//中序遍历线索二叉树, T指向头结点
void ReInOrderTraverse_Thr_1(BiThrTree T);//逆序遍历中序线索树, T指向头结点
void ReInOrderTraverse_Thr_2(BiThrTree T);//逆序遍历中序线索树, T指向头结点
void InThreading(BiThrTree *p, BiThrTree *pre); //中序遍历二叉树,并将其中序线索化
BiThrTree InOrderThreading(BiThrTree T);//创建头结点,并将二叉树改造成中序线索树 
void CreateBiTree(BiThrTree *bt);//生成一棵二叉排序树(输入单个字符,以#结束)
BiThrTree NewBiTree(ElemType x);//构造一个数据域为x的新结点
void Insert(BiThrTree *b, BiThrTree s);//在二叉排序树中插入新结点s
void InOrderPrint(BiThrTree p); //中序遍历输出结点(递归)


int main(void)
{
    BiThrTree bt = NULL;
    BiThrTree BT = NULL;
    BiThrTree s = NULL;
    int i;


    CreateBiTree(&bt);//生成一棵二叉排序树(输入单个字符,以#结束)
    InOrderPrint(bt); //中序遍历输出结点(递归)
    printf("\n");


    BT = InOrderThreading(bt);//中序遍历二叉树,并将其中序线索化
    InOrderTraverse_Thr_1(BT);//中序遍历线索二叉树的非递归算法, T 指向头结点
    printf("\n");
    InOrderTraverse_Thr_2(BT);//中序遍历线索二叉树的非递归算法, T 指向头结点
    printf("\n");
    InOrderTraverse_Thr_3(BT);//中序遍历线索二叉树的非递归算法, T 指向头结点
    printf("\n");
    InOrderTraverse_Thr_4(BT);//中序遍历线索二叉树的非递归算法, T 指向头结点
    printf("\n");
    ReInOrderTraverse_Thr_1(BT);//逆序遍历中序线索树, T指向头结点
    printf("\n");
    ReInOrderTraverse_Thr_2(BT);//逆序遍历中序线索树, T指向头结点
    
    system("PAUSE");
    return 0;
}


BiThrTree Successor(BiThrTree p)//返回结点p的后继
{
if (p->RTag == Thread) //由后继线索直接得到
return p->rchild;

//后继为右子树的第一个结点(最左边结点)
p = p->rchild;
while (p->LTag == Link) 
{
p = p->lchild;
}
return p;



void InOrderTraverse_Thr_1(BiThrTree T)//中序遍历线索二叉树, T指向头结点
{
BiThrTree p = T->lchild;

while (p->LTag == Link) //先找到第一个结点(最左边结点) 
{
p = p->lchild;
}
while (p != T)
{
printf("%c ", p->data); 
p = Successor(p);//返回结点p的后继
}
}




void InOrderTraverse_Thr_2(BiThrTree T)//中序遍历线索二叉树, T指向头结点
{
BiThrTree p = T->lchild;

while (p->LTag == Link) //先找到第一个结点(最左边结点) 
{
p = p->lchild;
}
while (p != T)
{
printf("%c ", p->data); 
if (p->RTag == Thread) //后继为右孩子 
{
p = p->rchild;
}
else//后继为右子树的第一个结点(最左边结点)
{
p = p->rchild;
while (p->LTag == Link) 
{
p = p->lchild;
}
}
}
}


void InOrderTraverse_Thr_3(BiThrTree T)//中序遍历线索二叉树, T指向头结点
{
BiThrTree p = T->lchild;

while (p != T)
{
while (p->LTag == Link) //寻找第一个结点(最左边的结点) 
{
p = p->lchild;
}

printf("%c ", p->data); 

while (p->RTag == Thread && p->rchild != T) //访问直接后续结点(直接由右指针指向) 
{
p = p->rchild;
printf("%c ", p->data); 
}

p = p->rchild; //访问右子树 
}
}


void InOrderTraverse_Thr_4(BiThrTree T)//中序遍历线索二叉树, T指向头结点
{
BiThrTree p = T->lchild;

while (p != T)
{
if (p->LTag == Link) //如果有左孩子,先遍历其左孩子 
{
p = p->lchild;
}
else //已经到了最左边 
{
printf("%c ", p->data);

while (p->RTag == Thread && p->rchild != T) //访问直接后续结点(直接由右指针指向) 
{
p = p->rchild;
printf("%c ", p->data);
}

p = p->rchild; //访问右子树 
}
}
}


BiThrTree Precursor(BiThrTree p)//返回结点p的前驱 
{
if (p->LTag == Thread) //由前驱线索直接得到
return p->lchild;

//前驱为左子树的最后一个结点(最右边结点)
p = p->lchild;
while (p->RTag == Link) 
{
p = p->rchild;
}
return p;



void ReInOrderTraverse_Thr_1(BiThrTree T)//逆序遍历中序线索树, T指向头结点
{
BiThrTree p = T->rchild; //p指向最后一个结点 


while (p != T)
{
printf("%c ", p->data); 
p = Precursor(p);//返回结点p的前驱 
}
}


void ReInOrderTraverse_Thr_2(BiThrTree T)//逆序遍历中序线索树, T指向头结点
{
BiThrTree p = T->rchild; //p指向最后一个结点 

while (p != T)
{
printf("%c ", p->data);
if (p->LTag == Thread) //由前驱线索直接得到
{
p = p->lchild;
}
else//前驱为左子树的最后一个结点(最右边结点)
{
p = p->lchild;
while (p->RTag == Link) 
{
p = p->rchild;
}
}
}
}


void InThreading(BiThrTree *p, BiThrTree *pre) //中序遍历二叉树,并将其中序线索化
{
if (*p != NULL)
{
InThreading(&(*p)->lchild, pre); //左子树线索化
// printf("测:%c  %c    ", (*p)->data, (*pre)->data);
if ((*p)->lchild == NULL) //若当前结点的左子树为空,则建立前驱线索
{
(*p)->LTag = Thread;
(*p)->lchild = (*pre);
}
else
{
(*p)->LTag = Link;
}

if (*pre != NULL && (*pre)->rchild == NULL)
{
(*pre)->RTag = Thread;
(*pre)->rchild = *p;
}

*pre = *p;    //中序向前遍历接点 ,保持pre指向p的前驱
        (*pre)->RTag = Link;//默认前驱结点右孩子非空
        
        InThreading(&(*p)->rchild, pre); //右子树线索化
}
}


BiThrTree InOrderThreading(BiThrTree T)//创建头结点,并将二叉树改造成中序线索树 
{
BiThrTree Thrt, pre;
Thrt = (BiThrTree)malloc(sizeof(BiThrNode));  //建立头结点
if (!Thrt)
{
printf("Out of space!");
exit(0);
}
Thrt->LTag = Link;
Thrt->RTag = Thread;
Thrt->rchild = Thrt; //右指针回指

if (! T)//若二叉树为空,则左指针回指
{
Thrt->lchild = Thrt; 
}
    else
{
Thrt->lchild = T;
        pre = Thrt;
        InThreading(&T, &pre);//中序线索化


        pre->rchild = Thrt; //最后一个结点线索化,此时pre指向最后一个结点
        pre->RTag = Thread;
        Thrt->rchild = pre; 
}   

return Thrt;
}


void CreateBiTree(BiThrTree *bt)//生成一棵二叉排序树(输入单个字符,以#结束)
{
BiThrTree s;
    ElemType x;
      
scanf("%c", &x);
    while (x != '#')
    {
        s = NewBiTree(x);//构造一个数据域为x的新结点
        Insert(bt, s);//在二叉排序树中插入新结点s
        scanf("%c", &x);
    }
}


BiThrTree NewBiTree(ElemType x)//构造一个数据域为x的新结点
{
BiThrTree s = (BiThrTree)malloc(sizeof(BiThrNode)); 
    
if (!s)
{
printf("Out of space!");
exit (1);
}
s->data = x;
s->lchild = s->rchild = NULL;
s->LTag = s->RTag = Link; //默认左,右孩子非线索


    return s;
}


void Insert(BiThrTree *b, BiThrTree s)//在二叉排序树中插入新结点s
{
if (*b == NULL)
*b = s;
else if ((*b)->data == s->data)//不做任何插入操作
return;
else if((*b)->data > s->data)//把s所指结点插入到左子树中
   Insert(&(*b)->lchild, s);
else               //把s所指结点插入到右子树中
   Insert(&(*b)->rchild, s);
}


void InOrderPrint(BiThrTree p) //中序遍历输出结点(递归)
{
if (p != NULL)
{
InOrderPrint(p->lchild); //遍历左子树
        printf("%c ", p->data);//输出该结点
InOrderPrint(p->rchild); //遍历右子树
}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值