线索二叉树的详解
目录
- 线索二叉树的详解
- 前言
- 一、线索二叉树是什么?
- 三种二叉树线索化实例图
- 二、实现线索二叉树
- 1.二叉树的线索化
- 2.线索二叉树的遍历
- 中序线索二叉树寻找遍历的首节点
- 中序线索二叉树寻找节点的直接后继
- 遍历中序线索二叉树
- 总结
前言
学了二叉树,我们发现,对二叉树的遍历是一个比较复杂的问题,需要用到递归或者栈才可以进行遍历,这样子的遍历实质上就是将二叉树化为一个有序的线性序列,在这个序列中,每一个节点有且只有一个前继节点(第一个节点除外),和一个后继节点(最后一个节点除外),但是这些节点不能直接的找到当前节点的前驱和后继信息,与此同时,二叉树在存储的过程中,会有很多的空节点,为了充分利用这些节点以及遍历更加方便,我们产生了线索二叉树
一、线索二叉树是什么?
为了区分二叉树的左孩子指针和右孩子指针是否为空,或者是否指向前驱节点或后继节点,我们将节点的结构改成5个域,在原二叉树的基础上添加左标志域Ltag和右标志域Rtag,他们是两个int型的数据域。
Lchild | Ltag | data | Rtag | Rchild |
---|
-
如果节点有左孩子,那么Lchild依然指向他的左孩子,否则指向遍历序列中他的前驱节点。
-
如果节点有右孩子,那么Rchild依然指向他的左孩子,否则指向遍历序列中他的后继节点。
-
Ltag和Rtag的定义如下:
Ltag : 等于0时,Lchild域指示节点的左孩子;等于1时,Lchild指示节点的遍历前驱。
Rtag : 等于0时,Rchild域指示节点的左孩子;等于1时,Rchild指示节点的遍历后继。
一些小概念
线索:指向前驱和后继节点的指针。
线索化:将空指针改为线索的过程。
三种二叉树线索化实例图
二、实现线索二叉树
以下都是以中序的方式进行的,先序和后序只需进行小小的改动即可
1.二叉树的线索化
首先我们会遇到的问题就是,当遇到空指针的时候我们应该如何填写相应的内容,可分为两种情况讨论。
①当遍历到左孩子指针域为空的时候,我们应该填入该节点的遍历前驱节点的指针,这时候我们可以提前声明一个全局变量pre来记录刚刚访问过的节点,在遍历过程中如果遇到左孩子为空的节点,就将pre赋给他的左孩子域,并且将Ltag设置为1。pre的初值应该为NULL,因为便利的首节点的前驱节点一定为空。
②当遍历过程中右孩子的指针域为空的时候,要填的节点应该为当前节点遍历序列的后继节点,但是我们现在无法确定,因此当前节点的右孩子指针域只能遍历到下一个节点的时候在填,并且当前节点就是pre节点的后继节点,所以我们只需要在遍历的过程中加上判断,回填pre的语句,若pre的右孩子节点为空,那么将当前的节点赋值给pre的右节点,同时pre节点的Rtag应该置为1
中序线索化代码如下(示例):
void Inthread(BiTree root)
{
//pre为全局变量,在函数外已经声明过
if(root != NULL)
{
Inthread(root->Lchild);//递归线索化root的左孩子
if(root->Lchild == NULL) //置前置线索
{
root->Lchild = pre;
root->Ltag = 1;
}
else{
root->Ltag = 0;
}
if(pre != NULL && pre->Rchlid == NULL)//置后置线索
{
pre->Rchlid = root;
pre->Rtag = 1;
}
pre = root;
Inthread(root->Rchlid);//递归线索化root的右孩子
}
}
2.线索二叉树的遍历
二叉树的中序遍历的思路就是:首先访问左子树,在访问根节点,最后访问右子树。
中序线索二叉树寻找遍历的首节点
按照这样的访问次序,首先访问的是树的最左下端,即沿着左孩子链走到最下端,找到第一个没有左孩子的节点(Ltag == 1)
代码如下(示例):
BiTree InFirst(BiTree bt)
{
BiTree p = bt;
if(p == NULL)
return(NULL);
//因为是中序遍历,所以最开始的节点一定是最左边的
while(p->Ltag == 0)
p = p->Lchild;
return(p);
}
中序线索二叉树寻找节点的直接后继
对于传进来的参数节点p,如果p没有右孩子那么直接可以获得他的后继节点,如果p有右子树,那么他的后继节点应该是p右子树中第一个遍历到的节点,有中序遍历我们可以知道,p的后继节点就是他右子树的最左下端第一个没有左孩子的节点
代码如下(示例):
BiTree InNext(BiTree p)
{
BiTree next,q;
if(p->Rtag == 1)
{
next = p->Rchlid;//直接利用线索
}
//右边的子树查找后继节点有问题
else{
//在p的右子树中查找最左下端的节点
for(q = p->Rchlid; q->Ltag == 0; q = q->Lchild);
next = q;
}
return(next);
}
遍历中序线索二叉树
最后利用上述的InFirst和InNext函数进行遍历
代码如下(示例):
BiTree TinOrder(BiTree root)
{
BiTree p;
p = InFirst(root);
while(p != NULL)
{
printf("%c",p->data);
p = InNext(p);
}
}
总结
通过上述的方法我们可以不用递归和栈从而实现对二叉树的遍历