线索二叉树
2016-07-13 09:53
302 查看
原理
将指向前驱和后继的指针称为线索,加上线索的二叉链表称为线索链表,相应的二叉树称为线索二叉树(Threaded Binary Tree)。将二叉树进行中序遍历后,将空指针域中右孩子指向其后继结点,再将所有空指针域中的lchild改为指向当前结点的前驱。其实线索二叉树等于将二叉树转变为一个双向链表,方便插入和删除。
对二叉树以某种次序遍历使其转变为线索二叉树的过程称作是线索化。
但是问题又来了,如何判断某结点的lchild是指向前驱还是左孩子,rchild是指向后继还是右孩子?于是,在每个节点再增设两个标志域ltag和rtag,这两个标志域只存放0或1布尔型变量,占用内存空间要小于指针变量。ltag为0时指向该结点的左孩子,为1时指向该结点的前驱;rtag为0时指向该结点的右孩子,为1时指向该结点的后继。
结构实现
二叉树的线索存储结构定义代码如下:/*二叉树的二叉线索存储结构定义*/ //Link==0表示指向左右孩子指针;Thread==1表示指向前驱或后继线索 typedef enum{Link,Thread} PointerTag; typedef struct BiThrNode{ //二叉线索存储结点结构 TElemType data; //结点数据 struct BiThrNode *lchild,*rchild; //左右孩子指针 PointerTag LTag; //左右标志 PointerTag RTag; }BiThrNode,*BiThrTree;
线索化的实质就是将二叉链表的空指针改为指向前驱或后继的线索,由于前驱和后继的信息只有在遍历该二叉树时才能得到,所以线索化的过程就是在遍历过程中修改空指针的过程。
中序遍历线索化的递归函数代码如下:
BiThrTree pre; /*全局变量,时钟指向刚刚访问过的结点*/ /*中序遍历进行中序线索化*/ void InThreading(BiThrTree p){ if(p){ //树不为空 InThreading(p->lchild); //先遍历左子树 if(! p->lchild){ //左子树为空,线索化,前驱 p->LTag=Thread; //Tag设为1 p->lchild=pre; //左孩子指针指向前驱 } if(! pre->rchild){ //前驱没有右孩子,线索化,后继 pre->RTag=Thread; //Tag设为1 pre->rchild=p; //右孩子指针指向后继 } pre=p; //保持pre指向p的前驱 InThreading(p->rchild); //最后遍历右子树 } }
需要注意的是,中间的代码,首先判断当前结点的左孩子是否为空,因其前驱刚访问过,所以可以方便的得到结点前驱并赋值;后当前结点的后继还未访问到,所以只能设置当前结点的前驱结点,判断前驱结点的右孩子是否为空,若为空,则将当前结点设置为前驱结点的后继。
对线索二叉树遍历相当于操作一个双向链表结构。和双向链表结构一样,在二叉树线索链表上添加一个头结点,令其lchild指向二叉树根结点,rchild域指向中序遍历时访问的最后一个结点,另外,令二叉树的中序遍历序列中的第一个节点lchild域指针和最后一个结点的rchild域指针指向头结点。这样,不仅可以从第一个结点起顺后继进行遍历,也可以从最后一个结点起顺前驱进行遍历。具体的遍历代码如下:
/*T指向头结点,头结点左链lchild指向根结点,头结点右链rchild指向中序遍历的最后一个结点。中序遍历二叉线索表表示的二叉树T*/ Status InOrderTraverse_Thr(BiThrTree T){ BiThrTree p; p=T->lchild; //p指向根结点,从根结点开始遍历 while(p != T){ //空树或遍历结束时 p==T while(p->LTag==Link) //当LTag==0时,循环到中序序列的第一个结点 p=p->lchild; printf("%c",p->data); //显示结点数据,可更改为其他操作 while(p->RTag==Thread && p->rchild != T){ p=p->rchild; printf("%c",p->data); } p=p->rchild; //p进至其右子树根 } return OK; }
代码解析:
1.代码第5~14行,while(p!=T) 的意思是循环结束当p指向头结点时,其实就是与T相等(T是指向头结点的指针)。
2.代码第6~7行,while(p->LTag==Link)这个循环,判断结点是否有左孩子,若有的话,指向下一个左孩子,没有则结束循环。
3.第8行,打印中序遍历的第一个结点。
4.第9~12行,while(p->RTag==Thread && p->rchild != T),若结点的右标志域为1且不指向头结点,则打印该结点的后继结点,否则退出循环。
5.第13行,p=p->rchild意味着p指向了当前结点的右子树的根。
其实是一个链表的扫描,时间复杂度为 O(n)O(n)。所以在实际问题中,如果所用的二叉树需经常遍历或查找结点时需要某种遍历序列中的前驱和后记,那么采用线索二叉链表的存储结构就是非常不错的选择。
相关文章推荐
- Nginx启动报错误unlink() “nginx.pid” failed (2: No such file or directory)
- 简单的写文本日志方法
- 李洪强iOS开发支付集成之银联支付
- 一个神奇的控件——Android CoordinatorLayout与Behavior使用指南
- xmlns:app="http://schemas.android.com/apk/res-auto"找不到?
- Unity3d如何制作声音开关按钮
- 各种算法和数据结构
- POJ 1655 Balancing Act
- Linux上Redis的安装过程以及注意事项
- Objective C类方法load和initialize的区别
- 以SpringMVC框架为中心疯狂扩展-01、Maven搭建webapp
- cordova iOS平台的插件开发
- css之absolute
- mkdir命令(建立一个目录) rmdir命令(删除一个目录) rm命令(删除文件)
- 【HDU3247】 Resource Archiver(DP+AC自动机+最短路)
- Git 常用命令
- Mysql 数据库crash恢复
- StringBuffer类的简入
- Html中使用自定义图片来实现checkbox显示
- IO流复制文本文件五种方法