线索二叉树
通过二叉树及其遍历算法我们了解到:
- 二叉树的遍历过程较为复杂,需要用到递归和栈;
- 二叉树的节点遍历序列是一个线性序列;
- 二叉链表难以找到前驱后继节点信息;
- 二n个结点的二叉链表中,有2n个指针域{n-1个非空指针域,n+1个空指针域}
【4注】证明:可以参考这位大佬的证明-->https://blog.csdn.net/zhangvalue/article/details/76359222
那么我们能不能利用空指针域,指向遍历序列中的前驱、后继节点,从而不再需要借助栈和递归也能方便的实现二叉树的遍历?
-----------------------------------------于是聪明的“古人”发明了‘线索二叉树’------------------------------------
-
✍背景:
首先,什么是二叉树的线索化,为什么要对二叉树线索化?
【答】二叉树是一种非线性结构,遍历二叉树几乎都是通过递归或者用栈辅助实现非递归的遍历。
用二叉树作为存储结构时,取到一个节点,只能获取节点的左孩子和右孩子,不能直接得到节点的任一遍历序列的前驱或者后继。
为了保存这种在遍历中需要的信息,我们利用二叉树中指向左右子树的空指针来存放节点的前驱和后继信息,就能实现二叉树的线索化
-
✍基本概念:
1. 指向遍历序列前驱、后继节点的指针成为线索
2.把空指针修改为线索的过程成为线索化;
3.经过线索化的二叉树称为线索二叉树;
4.含有线索的二叉链表叫做线索链表;
-
✍结点结构:
【注】: Ltag{0:指示该结点的左孩子; 1:指示该结点的前驱节点}
Rtag{0:指示该结点的右孩子; 1:指示该结点的后继节点}
-
✍类型:
- 先序二叉树 2. 中序二叉树 3. 后序二叉树
- 举例说明:(本文针对中序线索二叉树我用蓝色标识前驱,绿色标识后继,不知道怎么的上传后这么模糊。。)
一直进行下去,最后是这样的:(下次还是找网图吧…自己画好丑~)
-
✍实现方式:
伴随着遍历而进行的线索化过程中,设两个指针:(p和pre) p:指向当前访问节点,pre指向上一个访问的结点,即p的前驱;
(p and pre)始终动态变化,但还是互为前驱后继。
在修改过程中:若p->Lchild为空,则将Ltag修改为指示前驱节点pre;若pre->Rchild为空,则修改为指向后继节点p;
因此我们的 中序线索化算法 就呼之欲出啦~:
void Inthread(Bitree p){
if(p!=NULL){
Inthread(p->Lchild) ;//线索化左子树
if(p->Lchild==NULL){
p->Lchild=pre;
p->Ltag=1;
}
if(pre->Rchild==NULL){
pre->Rchild=p;pre->Rtag=1;
}
pre=p;//记得动态变化
Inthread(p->Rchiild); //线索化右子树
}
}
-
✍寻找访问的第一个节点:
在中序遍历中,第一个结点是树中处于“最左下方”的节点,从根沿着左孩子指针一直遍历到没有左孩子的节点即为所求。
BiTnode *Infirst(Bitree root){
BiTnode *p=root;
if(!p){
return NULL; //找到无左孩子为止
}
while(p->Ltag==0){ //有左孩子就继续
p=p->Lchild;
}
return p;
}
-
✍中序线索二叉树中找节点的后继:
1.若当前访问的节点无右孩子,则Rchild指向后继
2.若有右孩子,右子树中“最左下端”节点即为其后继节点(such as :A有右孩子,他的后继为E)
BiTnode *Innext(BiTnode *p){
if(p->Rtag==1){
Next=p->Rchild;//为1表示木有右孩子,右孩子指针就是后继
}
else{
for(q=p->Rchild;q->Ltag==0;q=q->Lchild);
Next=q; //有右孩子则遍历查找最左节点
}
return Next;
}
那么中序线索二叉树的遍历算法就是:--->
void LDR(Bitree root){
BiTnode *p;
p=Infirst(root);//找到第一个访问节点
while(p){
visit(p);
p=Innext(p);
}
}
-
✍实例程序
Input : 1. ABC##DE#G##F### 2.ABD#G###CE#H##F##
Output: -->C-->B-->E-->G-->D-->F -->D-->G-->B-->A-->E-->H-->C
-
#include<stdio.h> #include<malloc.h> #define Maxsize 100 typedef char Elemtype; typedef struct node{ Elemtype data; int Ltag,Rtag;//为0表示孩子,为1指示前驱后继 struct node *lchild; struct node *rchild; }BiTnode,*Bitree; int index=0; char str[Maxsize]; Bitree pre; //创建二叉树 int CreateTree(Bitree &root){ char ch; ch=str[index++]; if(ch=='#'){ root=NULL; } else{ root=(Bitree)malloc(sizeof(BiTnode)); if(!root){ return -1; } root->data=ch; CreateTree(root->lchild); CreateTree(root->rchild); } return 0; } //中序线索化以p为根节点的树 void Inthread(Bitree &p){ if(p!=NULL){ Inthread(p->lchild) ;//线索化左子树 //判断左指针域 if(p->lchild==NULL){ p->lchild=pre; p->Ltag=1; } else{ p->Ltag=0; } //判断右指针域 if(pre->rchild==NULL){ pre->rchild=p; pre->Rtag=1; } else{ p->Rtag=0; } //动态变化 pre=p; Inthread(p->rchild); //线索化右子树 } } //查找访问的第一个节点(中序遍历第一个节点是最左下端的节点) Bitree Infirst(Bitree root){ BiTnode *p=root; if(!p){ return NULL; //找到无左孩子为止 } while(p->Ltag==0){ //有左孩子就继续 p=p->lchild; } return p; } //找后继 Bitree Innext(Bitree p){ Bitree Next=(Bitree)malloc(sizeof(BiTnode)); Bitree q=(Bitree)malloc(sizeof(BiTnode)); if(p->Rtag==1){ Next=p->rchild; } else{ for(q=p->rchild;q->Ltag==0;q=q->lchild); //到右子树找最左节点 Next=q; } return Next; } int main(){ Bitree T,p; pre=(Bitree)malloc(sizeof(BiTnode)); pre->rchild=NULL; printf("输入需建二叉树先序序列\n"); scanf("%s",str); CreateTree(T); Inthread(T); p=Infirst(T); while(Innext(p)){ printf("-->%c",p->data); p=Innext(p); } }
-
✍运行结果:
-
✍正经致谢: