在介绍线索二叉树前,我们先简单地一下普通二叉树,普通二叉树的建立后如下:
从上图可以看到,总共有2n个指针,其中非空指针为n-1个,空指针却为n+1个,这造成了空间上极度的浪费,
为了能把这些空指针利用起来,我们可以将这些空指针指向其前驱节点或者后继节点。
1、线索二叉树的节点结构如下所示:
ltag | lchild | data | rchild | rtag |
我们规定:
当ltag=0时,lchild指向左孩子,当ltag=1时,lchild指向前驱节点;
当rtag=0时,rchild指向右孩子,当rtag=1时,rchild指向后继节点;
于是节点结构可定义如下:
typedef enum{link,thread}pointerTag;
typedef char Elemtype;
typedef struct ThreadNode
{
Elemtype data;
int ltag, rtag;
struct ThreadNode *lchild, *rchild;
}ThreadNode,*ThreadTree;
2、线索二叉树的建立
线索二叉树的建立与普通二叉树的建立基本相同,我们这里采用前序遍历的方式建立线索二叉树。
代码如下:
void CreateThreadTree(ThreadTree &T)
{
char c;
cin.get(c);
if (' ' == c)//叶结点
{
T = NULL;
}
else
{
T = new ThreadNode;
T->data = c;
T->ltag = link;
T->rtag = link;
CreateThreadTree(T->lchild);
CreateThreadTree(T->rchild);
}
}
假设我们通过上述代码建立了下图所示的二叉树:
3、二叉树的线索化
线索化的实质就是将二叉链表中的空指针改为指向前驱或后继的线索。由于前驱和后继信息只有在遍历该二叉树时才能得到,所以,线索化的过程就是在遍历的过程中修改空指针的过程,一般采用中序遍历,设当前节点为T,则在遍历过程中可设置一个全局变量pre指针来指向当前节点的前驱节点。
(1)、头指针初使化
为了初使化pre指针,以及在遍历二叉树的过程当中更加方便实现递归,我们需要先建立一个头结点来指向root结点。
我们通过下图来进行讲解:
头结点的和pre指针的初使化:
1、p->ltag = 0;
2、p->lchild = T
3、p->rtag = 0;
4、p->rchild = p;
5、pre = p;//关键所在,初始化了pre
接下来我们还是用图来讲解中序遍历线索化二叉树:
(2)、设置前驱节点
首先我们采用中序遍历到第一个要处理的节点D处,即当前节点指针T所在,此时,T->lchild = NULL,于是我们令:1、T->ltag = thread,
2、T->lchild = pre //(即将T->lchild指向T的前驱节点)
3、pre = T;
(3)、设置后继节点
接着,因为pre->rchild = NULL;
1、pre->rtag = thread;
2、pre->rchild = T;//设置后继节点
(4)、头结点的善后工作
依此类推,我们来到了最后一个节点F处
1、pre->rchild = p;//最后一结点指向头结点
2、pre->rtag = thread;
3、p->rchild = pre; //头结点指向最后一结点
最后构成的线索二叉树如下图所示:
整个二叉树线索化的代码如下:
void InThreading(ThreadTree &T)
{
//T代表当前结点,pre代表前继结点
if ( T )
{
InThreading(T->lchild);//递归线索化 左孩子
if (!T->lchild)//若左孩子为空,则将lchild指向前驱结点
{
T->ltag = thread;
T->lchild = pre;
}
if (!pre->rchild)//若有孩子为空,则将rchild指向后继结点
{
pre->rtag = thread;
pre->rchild = T;
}
pre = T;
InThreading(T->rchild);//递归线索化 右孩子
}
}
void InOrderThreading(ThreadTree &p,ThreadTree &T)
{
p = new ThreadNode;
p->ltag = link;
p->rtag = thread;//设置头结点的右孩子指向自己
p->rchild = p;
if (!T)//空树
{
p->lchild = p;
}
else
{
p->lchild = T;//设置头结点的左孩子指向根节点root
pre = p;
InThreading(T);
//善后工作
pre->rchild = p;//最后一结点指向头结点
pre->rtag = thread;
p->rchild = pre; //头结点指向最后一结点
}
}
4、中序遍历输出线索二叉树
使用线索二叉树中的线索,输出二叉树的各个元素。
实现代码如下:
//中序遍历输出二叉树
void InOrderTraversal(ThreadTree &p)
{
ThreadTree T;
T = p->lchild;
while (T != p)//当前结点不等于头结点
{
while (T->ltag == link)//一直循环直到最后一个左孩子
{
T = T->lchild;
}
visit(T);//输出
if (T->rtag == thread && T->rchild != p)//当右孩子为空时,输出后继结点
{
T = T->rchild;
visit(T);
}
T = T->rchild;//有孩子或者后继结点
}
}
5 、线索二叉树实现完整代码
#include <iostream>
using namespace std;
typedef enum{link,thread}pointerTag;
typedef char Elemtype;
typedef struct ThreadNode
{
Elemtype data;
int ltag, rtag;
struct ThreadNode *lchild, *rchild;
}ThreadNode,*ThreadTree;
//全局变量,表示前驱结点
ThreadTree pre;
//以前序遍历方式建立二叉树
void CreateThreadTree(ThreadTree &T)
{
char c;
cin.get(c);
if (' ' == c)//叶结点
{
T = NULL;
}
else
{
T = new ThreadNode;
T->data = c;
T->ltag = link;
T->rtag = link;
CreateThreadTree(T->lchild);
CreateThreadTree(T->rchild);
}
}
//中序遍历线索化二叉树
//ps:线索化的实质就是将二叉链表中的空指针改为指向前驱或后继的线索。
//由于前驱和后继信息只有在遍历该二叉树时才能得到,所以,线索化的过程就是在遍历的过程中修改空指针的过程。
void InThreading(ThreadTree &T)
{
//T代表当前结点,pre代表前继结点
if ( T )
{
InThreading(T->lchild);//递归线索化 左孩子
if (!T->lchild)//若左孩子为空,则将lchild指向前驱结点
{
T->ltag = thread;
T->lchild = pre;
}
if (!pre->rchild)//若有孩子为空,则将rchild指向后继结点
{
pre->rtag = thread;
pre->rchild = T;
}
pre = T;
InThreading(T->rchild);//递归线索化 右孩子
}
}
void InOrderThreading(ThreadTree &p,ThreadTree &T)
{
p = new ThreadNode;
p->ltag = link;
p->rtag = thread;//设置头结点的右孩子指向自己
p->rchild = p;
if (!T)//空树
{
p->lchild = p;
}
else
{
p->lchild = T;//设置头结点的左孩子指向根节点root
pre = p;
InThreading(T);
//善后工作
pre->rchild = p;//最后一结点指向头结点
pre->rtag = thread;
p->rchild = pre; //头结点指向最后一结点
}
}
//对二叉树结点进行操作
void visit(ThreadTree T)
{
cout << T->data;
}
//中序遍历输出二叉树
void InOrderTraversal(ThreadTree &p)
{
ThreadTree T;
T = p->lchild;
while (T != p)//当前结点不等于头结点
{
while (T->ltag == link)//一直循环直到最后一个左孩子
{
T = T->lchild;
}
visit(T);//输出
if (T->rtag == thread && T->rchild != p)//当右孩子为空时,输出后继结点
{
T = T->rchild;
visit(T);
}
T = T->rchild;//有孩子或者后继结点
}
}
int main()
{
ThreadTree p, T = NULL;
cout << "请以前序遍历方式输入数据:";
CreateThreadTree(T);
InOrderThreading(p, T);
cout << "线索中序遍历输出数据:";
InOrderTraversal(p);
cout << endl;
system("pause");
return 0;
}