对于一具有n个节点的二叉树来说,采用二叉链存储结构时,每个节点有两个指针域总共有2n个指针域,而只有n-1个节点被有效指针所指(n个节点只有根节点没有被有效指针域所指)
故一共有n+1个空指针域,造成空间上的浪费。
对二叉树遍历产生的序列是个线性序列。如果用这些空链域存放指向节点的前驱节点和后驱节点,这样遍历时会方便许多。
先放个链接:中序线索化二叉树的动画过程。方便后面算法的理解。
由于遍历的方式不同,产生的遍历线性序列不同。重新定义二叉树的节点如下
typedef struct node { ElemType data; //节点的数据域 int ltag,rtag; //增加的线索标记 struct node *lchild; //当ltag=0时lchild代表左节点,ltag=1时代表没有左节点lchild代表的是前驱节点 struct node *rchild; //当rtag=0时rchild代表右节点,rtag=1时代表没有右节点rchild代表的是后继节点 } TBTNode;//线索数的节点类型定义
为了算法的设计方便我们在线索化二叉树上再增加一个头节点。头节点的data域为空,lchild指向没有线索时的根节点,将ltag设为0,而rchild指向某种遍历方式中的最后一个节点,rtag=1。
对同一棵树的遍历方式不同,所得的线索树也不同。二叉树有线序中序后序三种,故线索树也有先序线索树,中序线索树,后序线索树三种。
建立线索树的实质其实就是遍历一颗二叉树,在遍历的过程中检查当前节点的指针域是否为空,为空将它们改成前驱节点或者是后驱节点。
而在三种线索化二叉树中
先序线索二叉树查找一个节点的先序后继节点很简单如图先序线索树 假设当前节点为p,当p->rtag==1时我们知道p->rchild表示后继节点,此时只需判断是否指向了头节点root若是代表p就是先序遍历中的最后一个节点。而当p->rtag==0若p->ltag==1则p的后继节点就是p->rchild否则后继节点是p->lchild。
先序遍历查找一个节点的先序前驱节点就不好办了,从下图先序线索树你可以看到求B、C的前驱节点在不知道双亲节点的情况下无法判断,对于DGEF四个点则是可以在不知双亲节点的情况下求得。
后序线索树存在同样的问题。查找后序前驱结点简单但查找查找后序后继节点也不大好办。
所以在实际应用中先序线索化和后序线索化用的少,中序线索化用的比较多。中序线索化中求前驱节点和后继节点的方法如下。
中序线索化程序单步调试过程
从下图知道节点D左孩子指向了头节点即有D->lchild==root,而D->ltag=1,由此我们知道D没有前驱节点,而D->rchild=G!=root且D->rtag==0而对于D的右子树而言只有一个节点G所以G是D的后序节点否则应该是D的右子树中序遍历下的第一个节点是D的后继节点。
完整代码:
//文件名:exp7-5.cpp #include <stdio.h> #include <malloc.h> #define MaxSize 100 typedef char ElemType; typedef struct node { ElemType data; //节点的数据域 int ltag,rtag; //增加的线索标记 struct node *lchild; //当ltag=0时lchild代表左节点,ltag=1时代表没有左节点lchild代表的是前驱节点 struct node *rchild; //当rtag=0时rchild代表右节点,rtag=1时代表没有右节点rchild代表的是后继节点 } TBTNode;//线索数的节点类型定义 //创建二叉树 void CreateTBTNode(TBTNode * &b,char *str) { TBTNode *St[MaxSize],*p=NULL; int top=-1,k,j=0; char ch; b=NULL; //建立的二叉树初始时为空 ch=str[j]; while (ch!='