线索二叉树的基本操作
//定义数据类型
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
{
if(!T=(BiTree)malloc(sizeof(BitNode))) return 0;
T->data = ch;
CreateTree(T->lchild));
CreateTree(T->rchild));
}
return 0;
}
----------------------------------------------------------------------------------------
中序遍历线索化,得到中序线索二叉树
//thr指向头结点,头结点左链lchild指向根结点,头结点右链rchild指向中序遍历的最后一个结点。
int InOrderThraverse_Thr(BiTree T)
{
BiTree thr;
thr->ltag=Link;
thr-rtag=Thread;
thr->rchild=thr;
if(!T)
thr-lchild=thr;
else{
pre=thr;
InThreading(T);
pre->rtag=Thread;
pre->rchild=thr;
thr->rchild=pre;
}
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); //递归右子树线索化
}
}
----------------------------------------------------------------------------------------
//查找前驱节点及后继节点
在中序线索二叉树上查找任意结点的中序前驱结点
对于中序线索二叉树上的任一结点,寻找其中序的前驱结点,有以下两种情况:
(1)如果该结点的左标志为1,那么其左指针域所指向的结点便是它的前驱结点;
(2)如果该结点的左标志为0,表明该结点有左孩子,根据中序遍历的定义,它的前驱结点
是以该结点的左孩子为根结点的子树的最右结点,即沿着其左子树的右指针链向下查找。
当某结点的右标志为1 时,它就是所要找的前驱结点。
BiTree InPreNode(BiTree &p){
BiTree pre;
pre=p->lchild;
if(!p->ltag)
while(!pre->rtag){
pre=pre->rchild;
}
}
return pre;
}
//在中序线索二叉树上查找任意结点的中序后继结点
对于中序线索二叉树上的任一结点,寻找其中序的后继结点,有以下两种情况:
(1)如果该结点的右标志为1,那么其右指针域所指向的结点便是它的后继结点;
(2)如果该结点的右标志为0,表明该结点有右孩子,根据中序遍历的定义,它的前驱结点
是以该结点的右孩子为根结点的子树的最左结点,即沿着其右子树的左指针链向下查找.
当某结点的左标志为1 时,它就是所要找的后继结点。
BiTree InPostNode(BiTree &p){
BiTree post;
post=p->rchild;
if(!p->rtag)
while(!post->ltag){
pre=pre->lchild;
}
}
return post;
}
--------------------------------------------------------------------------------------
//在中序线索二叉树上查找值为x的结点(遍历线索二叉树)
利用在中序线索二叉树上寻找后继结点和前驱结点的算法,就可以遍历到二叉树的所有结
点。比如,先找到按某序遍历的第一个结点,然后再依次查询其后继;或先找到按某序遍历的
最后一个结点,然后再依次查询其前驱。这样,既不用栈也不用递归就可以访问到二叉树的所
有节点。
在中序线索二叉树上查找值为x 的结点,实质上就是在线索二叉树上进行遍历,将访问结点
的操作具体写为那结点的值与x 比较的语句。
BiTree Insearch(BiTree head,chare){
BiTree p;
p=head->lchild;
while(p->ltag==0&&p!=head) p=head为遍历结束标志
p=p->lchild; 找到中序遍历的第一个节点(最左孩子)
while(p->data!=e&&p!=head) p=head为遍历结束标志
InPostNode(p);
if(p->data==e)
return p;
else{
printf("没找到");
return 0;
}
-----------------------------------------------------------------------------------------
//在中序线索二叉树上的插入与删除
线索二叉树的更新是指,在线索二叉树中插入一个结点或者删除一个结点。一般情况下,这些操作有
可能破坏原来已有的线索,因此,在修改指针时,还需要对线索做相应的修改。一般来说,这个过程的代
价几乎与重新进行线索化相同。这里仅讨论一种比较简单的情况,即在中序线索二叉树中插入一个结点p,
使它成为结点s 的右孩子。
下面分两种情况来分析:
(1)若s 的右子树为空,如图6.13 (a)所示,则插入结点p 之后成为图6.13 (b)所示的情形。在这种情况中,
s 的后继将成为p 的中序后继,s 成为p 的中序前驱,而p 成为s 的右孩子。二叉树中其它部分的指针
和线索不发生变化。
(2)若s 的右子树非空,如图6.14 (a)所示,插入结点p 之后如图6.14 (b)所示。S 原来的右子树变成p 的右
子树,由于p 没有左子树,故s 成为p 的中序前驱,p 成为s 的右孩子;又由于s 原来的后继成为p 的后
继,因此还要将s 原来的本来指向s 的后继的左线索,改为指向p。
下面给出上述操作的算法。
void InsertThrRight(BiThrTree s,BiThrTree p)
{
BiThrTree w;
p->rchild=s->rchild;
p->rtag=s->rtag;
p->lchild=s;
p->ltag=1;
s->rchild=p;
s->rtag=0;
if(p->rtag==0)
{
w=InPostNode(p);
w->lchild=p;
}
}