一.概念
什么是线索二叉树?在我们写二叉树的时候,我们在末端结点的左右儿子用了NULL
来表示,以告知结束。如果我们将这个左右儿子指向的地址指向下一个该遍历的结点,那岂不是就能提高结点的利用率?一个结点,在它的之前遍历的那个结点,叫这个结点的前驱
,同理,在它之后遍历的第一个结点,叫做后继
。我觉得这幅图画得很好:
实质上来说,线索二叉树,能将一个二叉树变成一个双向链表。其中指向前驱和后记的指针,叫做线索
, 加上线索的二叉树就叫线索二叉树
二.实现
为了实现我们的线索二叉树,我们要重新定义下一棵树:一棵树的儿子结点可以指向它真正的儿子,也就是左右结点。那么在线索二叉树下,它能指向下一个该遍历的结点。所以为了区分,我们的一棵树应该表示为:左儿子
、左儿子类型
、结点内容
、右儿子
、右儿子类型
。其中儿子类型告知了这个儿子到底是指向的真儿子,还是说是是父辈
的结点。
而且,线索二叉树一般由中序
或者后序
的方式创建。
我们按照中序的方式来遍历。该结点的前驱,应该是它遍历左左子后,堆栈的最顶层那个结点(毕竟是最后一个)。该结点的后去,应该是它遍历右儿子后的栈底的那个结点(第一个)。
三.代码实现
网上找的代码:
#include <stdio.h>
#include <stdlib.h>
#define ERROR 0
#define OK 1
typedef enum{Link, Thread} PointerTag; //link = 0表示指向左右孩子指针
//Thread = 1表示指向前驱或后继的线索
typedef struct BitNode
{
char data; //结点数据
struct BitNode *lchild; //左右孩子指针
struct BitNode *rchild;
PointerTag ltag; //左右标志
PointerTag rtag;
}BitNode, *BiTree;
BiTree pre; //全局变量,始终指向刚刚访问过的结点
//前序创建二叉树
void CreateTree(BiTree *t)
{
char ch;
scanf("%c", &ch);
if(ch == '#')
{
*t = NULL;
}
else
{
(*t) = (BiTree)malloc(sizeof(BitNode));
if((*t) == NULL)
{
return;
}
(*t)->data = ch;
CreateTree(&((*t)->lchild));
CreateTree(&((*t)->rchild));
}
}
//t指向头结点,头结点左链lchild指向根结点,头结点右链rchild指向中序遍历的最后一个结点。
//中序遍历二叉线索树表示的二叉树t
int InOrderThraverse_Thr(BiTree t)
{
BiTree p;
p = t->lchild; //p指向根结点
while(p != t)
{
while(p->ltag == Link) //当ltag = 0时循环到中序序列的第一个结点
{
p = p->lchild;
}
printf("%c ", p->data); //显示结点数据,可以更改为其他对结点的操作
while(p->rtag == Thread && p->rchild != t)
{
p = p->rchild;
printf("%c ", p->data);
}
p = p->rchild; //p进入其右子树
}
return OK;
}
//中序遍历进行中序线索化
void InThreading(BiTree p)
{
if(p)
{
InThreading(p->lchild); //递归左子树线索化
if(!p->lchild) //没有左孩子
{
p->ltag = Thread; //前驱线索
p->lchild = pre; //左孩子指针指向前驱,这里是第3步
}
if(!pre->rchild) //没有右孩子
{
pre->rtag = Thread; //后继线索
pre->rchild = p; //前驱右孩子指针指向后继(当前结点p)
}
pre = p;
InThreading(p->rchild); //递归右子树线索化
}
}
//建立头结点,中序线索二叉树
int InOrderThread_Head(BiTree *h, BiTree t)
{
(*h) = (BiTree)malloc(sizeof(BitNode));
if((*h) == NULL)
{
return ERROR;
}
(*h)->rchild = *h;
(*h)->rtag = Link;
if(!t) //如果为NULL
{
(*h)->lchild = *h;
(*h)->ltag = Link;
}
else
{
pre = *h;
(*h)->lchild = t; //第一步
(*h)->ltag = Link;
InThreading(t); //找到最后一个结点
pre->rchild = *h; //第四步
pre->rtag = Thread;
(*h)->rchild = pre; //第二步
}
}
int main(int argc, char **argv)
{
BiTree t;
BiTree temp;
printf("请输入前序二叉树的内容:\n");
CreateTree(&t); //建立二叉树
InOrderThread_Head(&temp, t); //加入头结点,并线索化
printf("输出中序二叉树的内容:\n");
InOrderThraverse_Thr(temp);
printf("\n");
return 0;
}