1.二叉链表作为存储结构时,只能找到结点的左右孩子信息,无法得到某一结点在遍历输出时的前驱和后继信息。
2.由下图可以看到,有n个结点的二叉链表必然存在n+1个指向NULL的空链域(图中三角形表示)
3.我们发现,当二叉树以中序遍历输出时,刚好呈现出:每隔一个结点就会有一个存在两个空指针的结点(红色字母表示)。
此时我们让存在空链域的结点,链域分别指向前驱和后继,便得到了中序线索树。在中序线索树上遍历二叉树,无需设堆栈。故若某程序中需要二叉树经常遍历、经常查找线性序列中的前驱和后继,则应采用线索链表作为存储结构。
二叉树中序线索化的实质是将二叉链表中的空指针改为指向前驱或后继的线索,而前驱和后继的信息只有在遍历时能够得到,因此中序线索化的过程就是在遍历过程中修改空指针的过程
为了方便,线索链表设置一个头结点,令其lchild指向二叉树的根节点,rchild指向中序遍历访问的最后一个结点。反之,令二叉树中序序列的第一个节点的lchild和最后一个结点的rchild均指向头结点
以下是二叉树中序线索化的代码实现:
#include "head.h"
//若结点有子树,链域指向子树,若结点无子树,链域指向前驱和后继
typedef enum PointerTag {Link , Thread};
typedef char ElemType;
typedef struct BiThrNode{ //二叉树的二叉线索存储表示
ElemType data;
BiThrNode *lchild, *rchild;
PointerTag LTag, RTag;
}BiThrNode,*BiThrTree;
//初始化链表,创建头结点
void InitBiThrTree(BiThrNode &head)
{
head = (BiThrTree)malloc(sizeof(BiThrNode)); //创建并初始化头结点
head ->LTag = Link;
head ->RTag = Thread;
head ->lchild = NULL;
head ->rdhild = head;
}
//创建二叉树,默认用户按前序遍历输入数据
void CreateBiTree(BIThrTree T) //把头结点传进来
{
char c;
scanf_s("%c",&c);
if( c == ' ')
T->lchild = NULL;
else
{
T->lchild = (BiThrTree)malloc(sizeof(BiThrNode));
(T->lchild) -> data = c;
CreateBiTree( (T->lchild)->lchild );
CreateBiTree( (T->lchild)->rchild );
}
}
//二叉树中序线索化核心代码
void InThreading(BiThrTree p, BiThrTree pre)附设一个指针pre指向刚刚访问过的结点//p是根节点
{
if( p ) // 不是空树
{
InThreading(p->lchild, p); //左子树线索化,向左走到尽头
if(! p -> lchild)
{
p->LTag = Thread; // 写入前驱线索
p->lchild = pre;
}
if(! pre->rchild) // 前驱结点 没有右子树
{
pre ->RTag = Thread; //前驱节点 写入后继线索
pre->rchild = p;
}
pre = p; //pre指向刚刚访问过的结点
InThreading(p->rchild, p); //右子树线索化
}
}
//二叉树中序线索化,需调用上面的核心代码,供用户使用的函数
void InOrderThreading(BiThrTree head)
{
BiThrTree T = head->lchild;//T是根节点
BiThrTree pre; //指向刚刚访问过的结点
pre = head; //初始化pre指针,使其指向头结点
InThreading(T, pre);
pre ->RTag = Thread; //最后一个节点线索化
pre ->rchild = head;
head ->rchild = pre;
}
//中序遍历线索二叉树
void InOrderTraverse_Thr(BiThrTree head)
{
BiThrTree p = head->lchild; //根节点放入p
while(p != head) //空树或遍历结束时 p==T
{
while(p ->LTag == Link) p = p->lchild; //向左走到尽头,并访问结点值
printf("%c", p->data);
while(p->RTag == Thread && p->rchild != head) //没有右子树,则访问后继结点
{
p = p->rchild;
printf("%c",p ->data);
}
p = p->rchild; //有右子树,则进入右子树
}
}