二叉树的线索化
线索二叉树
6.4 线索化二叉树从前面的讨论可知,遍历二叉树就是将非线性结构的二叉树线性化,即按一定规则将二叉树中的结点排列成一个线性序列依次访问。
如图6.20(a)所示的二叉树,经中序遍历得到线性序列:BADEC,经前序遍历得到线性序列:ABCDE,经后序遍历得到线性序列:BEDCA。
在这些线性序列中,二叉树中的每个结点(除第一个和最后一个外)有且仅有唯一的一个前趋和唯一的一个后继,很容易找到各个结点的直接前驱和直接后继。
但当以二叉链表作为二叉树的存储结构时,只能找到结点的左、右孩子,而不能直接找到前驱和后继,只有在遍历的动态过程中得到这些信息。
如果将这些信息在第一次遍历时保存起来,在需要再次对二叉树进行“遍历”时就可以将二叉树视为线性结构进行访问,从而简化遍历操作。
那么,如何存储遍历中得到的结点前驱和后继的信息呢?一个简单的办法是在每个结点上增加两个指针域fwd和bkwd,分别指向存储遍历中得到的结点前驱和后继。
fwd L child data R child bkwd这是采用多重链表来表示二叉树。
这种方法虽简单易行,但这种结构的存储密度将大大降低,浪费存储空间。
另一种方法,是利用原有链域L child 和R child的空链域。
在n个结点的二叉链表中有2n个孩子链域,其中仅有n-1个链域是用来指示结点的左右孩子,而另外n+1个链域是空链域。
现在把这些空链域利用起来,使其指向结点的前驱或后继;对那些原来就不为空的链域,则仍然指向左或右孩子。
如果把指向前驱和后继的指针称为线索(Thread),那么,如何区分指向左、右孩子的指针和指向前驱、后继的线索呢?在原结点结构上增加标志域定义为:0 Lchild为左指针,指向左孩子0 Rchild为右指针,指向右孩子ltag=rtag=1 Lchild为左线索,指向前驱 1 Rchild为右线索,指向后继以这种结点构成的二叉链表作为二叉树的存储结构,叫做线索链表,其C语言类型说明如下:Typedef struct ThreadTNode{enum{0,1} ltag, rtag;Elem Type data;Struct ThreadTNode *Lchild, *Rchild;}ThreadTNode, *ThreadTree;为了节省内存空间,我们用C语言的位段方法将结点中的左标志域和右标志域与数据域合并在一个存储单元中(即各用一位表示左标志和右标志,其余各位表示结点值)。
实验8 线索二叉树的创建和遍历
实验八线索二叉树的创建和遍历1.实验目的(1)掌握二叉树的线索链表存储结构。
(2)能够实现二叉树的线索链表的创建、遍历等基本操作。
2.实验内容编程实现二叉树的线索链表存储表示的基本操作,包括线索二叉树的创建与遍历。
3.实验要求(1)根据实验内容编写程序,上机调试并获得运行结果(2)撰写实验报告4.准备工作本次实验将会构造此二叉树,并会对此二叉树进行遍历5.关键步骤与算法(1)构造线索二叉树算法步骤;若某节点的左孩子节点指针域(lchild)为空,就可以利用它来指出该节点在某种遍历序列中的直接前驱的存储地址;若某节点的右孩子指针域(rchild)为空,就可以利用它来指出该节点在某种遍历序列中的直接后继的存储地址。
那些非空的指针域仍存放指向该节点左、右孩子节点的指针域。
这样,就得到了一棵线索二叉树。
算法如下;1.//按先序遍历序列输入树中各节点的值,构造线索二叉树2.BiThrTree* CreateBiThrTree()3.{4. BiThrTree *s;5. DataType ch;6. scanf("%c",&ch);7.8.if(ch == '#')9. exit(0);10.if(ch == '@')11. s = NULL;12.else if (ch=='\n')13. getchar();14.15.else16. {17. s = (BiThrTree *)malloc(sizeof(BiThrTree));18.if(!s)19. exit(ERROR);20. s->data = ch;21. s->lflag = 0;22. s->rflag = 0;23. s->lchild = CreateBiThrTree();24. s->rchild = CreateBiThrTree();25. }26.return s;27.}(2) 将二叉树中序线索化现将树递归到左子树然后再判断这个树的左右子树是否为空,如果为空则把它们的标志设为1,代表线索,首先设一个current代表前驱结点,然后判断它是否为空,如果为空则进行下一次递归,如果不为空,则判断左标志和右标志如果左标志为线索,则让前驱节点的右孩子指向当前节点,如果右标志为1,则让当前节点的左孩子指向前驱结点算法如下;1.void InThreading(BiThrTree *t)2.{3.if(t != NULL)//先判断树为不为空,如果为空,则不执行函数4. {5. InThreading(t->lchild);//递归调用左子树进行线索化6.if(t->lchild == NULL)7. t->lflag = 1;8.if(t->rchild == NULL)9. t->rflag = 1;10.//以上两步是判断这个结点的左右结点是否为空,若为空,把他们对应的flag标上111.if(current != NULL)//因为要用到current所以要判断一下,且current只有当t回到倒数第二个结点时,才会不为NULL12. {13.if(current->rflag == 1)14. current->rchild = t;//前驱结点的右孩子指针指向当前节点t15.if(t->lflag == 1)16. t->lchild = current;//现结点t的左孩子指针指向前驱结点17. }18. current = t;//让前驱结点为t为下一次执行函数做准备19. InThreading(t->rchild);//递归调用右子树进行线索化20. }21.}6.源代码1.#include<malloc.h>2.#include<stdio.h>3.#include<stdlib.h>4.#include<io.h>5.#define ERROR -16.7.typedef char DataType;8.typedef struct BiThrNode//二叉树的二叉链表的存储结构9.{10. DataType data;11.struct BiThrNode *lchild,*rchild;12.int lflag,rflag;//左右标志,值为0表示指针,值为1表示线索13.}BiThrTree;14.BiThrTree *current = NULL;15.//检查if的==16.//按先序遍历序列输入树中各节点的值,构造线索二叉树17.BiThrTree* CreateBiThrTree()18.{19. BiThrTree *s;20. DataType ch;21. scanf("%c",&ch);22.23.if(ch == '#')24. exit(0);25.if(ch == '@')26. s = NULL;27.else if (ch=='\n')28. getchar();29.30.else31. {32. s = (BiThrTree *)malloc(sizeof(BiThrTree));33.if(!s)34. exit(ERROR);35. s->data = ch;36. s->lflag = 0;37. s->rflag = 0;38. s->lchild = CreateBiThrTree();39. s->rchild = CreateBiThrTree();40. }41.return s;42.}43.//将二叉树前序线索化(递归)44.void PreThreading(BiThrTree *t)45.{46.if(t != NULL)//先判断树为不为空,如果为空,则不执行函数47. {48.if(t->lchild == NULL)49. t->lflag = 1;50.if(t->rchild == NULL)51. t->rflag = 1;52.//以上两步是判断这个结点的左右结点是否为空,若为空,把他们对应的flag标上153.if(current != NULL)//因为要用到current所以要判断一下,且current只有当t回到倒数第二个结点时,才会不为NULL54. {55.if(current->rflag == 1)56. current->rchild = t;//前驱结点的右孩子指针指向当前节点t57.if(t->lflag == 1)58. t->lchild = current;//现结点t的左孩子指针指向前驱结点59. }60. current = t;//让前驱结点为t为下一次执行函数做准备61.if(t->lflag == 0)62. PreThreading(t->lchild);//递归调用右子树进行线索化63.if(t->rflag == 0)64. PreThreading(t->rchild);65. }66.}67.//遍历前序线索二叉树68.void PreVisitThrtree(BiThrTree *t)69.{70.while(t != NULL)//大循环71. {72.while(t->lflag == 0)73. {74. printf("%c\t",t->data);75. t = t->lchild;76. }77. printf("%c\t",t->data);78. t = t->rchild;79. }80.}81.82.//检查if的==83.//将二叉树中序线索化(递归)84.void InThreading(BiThrTree *t)85.{86.if(t != NULL)//先判断树为不为空,如果为空,则不执行函数87. {88. InThreading(t->lchild);//递归调用左子树进行线索化89.if(t->lchild == NULL)90. t->lflag = 1;91.if(t->rchild == NULL)92. t->rflag = 1;93.//以上两步是判断这个结点的左右结点是否为空,若为空,把他们对应的flag标上194.if(current != NULL)//因为要用到current所以要判断一下,且current只有当t回到倒数第二个结点时,才会不为NULL95. {96.if(current->rflag == 1)97. current->rchild = t;//前驱结点的右孩子指针指向当前节点t98.if(t->lflag == 1)99. t->lchild = current;//现结点t的左孩子指针指向前驱结点100. }101. current = t;//让前驱结点为t为下一次执行函数做准备102. InThreading(t->rchild);//递归调用右子树进行线索化103. }104.}105.//检查if的==106.//遍历中序线索二叉树107.void InVisitThrtree(BiThrTree *t)108.{109.while(t != NULL)//大循环110. {111.while(t->lflag == 0)//小循环1;此循环第一次进行时首先要去找到最左节点112. t = t->lchild;113.if(t == NULL)114. exit(ERROR);115. printf("%c\t",t->data);116.while(t->rflag == 1 && t->rchild != NULL)//小循环2117. {118. t = t->rchild;119. printf("%c\t",t->data);120. }121. t = t->rchild;122./*123.两种情况:124. 1.如果t的右子树为空则t直接指向t的中序后继结点.125. 2.如果t的右子树不为空,即t->rflag为0,那么退出这个小循环2回到大循环中,再到小循环1中去找t的右子树的最左下的结点.126. */127. }128.}129.130.//将二叉树后序线索化(递归)131.void PostThreading(BiThrTree *t)132.{133.if(t != NULL)//先判断树为不为空,如果为空,则不执行函数134. {135. PreThreading(t->lchild);136. PreThreading(t->rchild);137.if(t->lchild == NULL)138. t->lflag = 1;139.if(t->rchild == NULL)140. t->rflag = 1;141.//以上两步是判断这个结点的左右结点是否为空,若为空,把他们对应的flag标上1142.if(current != NULL)//因为要用到current所以要判断一下,且current只有当t回到倒数第二个结点时,才会不为NULL143. {144.if(current->rflag == 1)145. current->rchild = t;//前驱结点的右孩子指针指向当前节点t146.if(t->lflag == 1)147. t->lchild = current;//现结点t的左孩子指针指向前驱结点148. }149. current = t;//让前驱结点为t为下一次执行函数做准备150. }151.}152.//遍历后序线索二叉树153.void PostVisitThrtree(BiThrTree *t)154.{155.if(t)156. {157.while(t->lchild != NULL && t->lflag == 0)158. {159. t = t->lchild;//先遍历到最左边的节点160. }161. }162.}163.//主函数164.void main()165.{166. BiThrTree *t,*s;167. printf("\t\t请按先序序列输入二叉树(如:ABC@@DE@G@@F@@@#)\n\t\t");168. t = CreateBiThrTree();169. InThreading(t);170. printf("\t\t按中序遍历输出线索二叉树:\n\t\t");171. InVisitThrtree(t);172. printf("\n");173.//getchar();174.//getchar();175. fflush(stdin);//这个操作必须要进行,或者是进行上面注释的那两步操作,要不然176. printf("\t\t请按先序序列输入二叉树(如:ABC@@DE@G@@F@@@#)\n\t\t");177. s = CreateBiThrTree();178. PreThreading(s);179. printf("\t\t按前序遍历输出线索二叉树:\n\t\t");180. PreVisitThrtree(s);181.}7.测试图8.实验总结然对于这一节,主要是讨论线索二叉树的建立以及遍历,对于二叉树的建立,主要有五个部分构成,分别是data,lchild,rchild,lflag,rflag,而它与二叉树不同的地方就是多了lflag和rflag的判断,当当前节点的左节点为空时一定会指向它的前驱结点,当前驱节点的右节点为空时,让前驱结点的右指针域指向当前节点,这就是线索化,而对于前序中序后序的线索化本质都是一样的。
线索二叉树
遍历 线索 二叉 树非
{
while(p->ltag==0) p=p->left; /*从根往下找到"最左"的结
点,即中序序列的开始结点*/
do
{
递归 算法
printf("%c",p->date);/*访问结点*/
p=succ(p);
}while(p!=NULL); }
返回
}
树
数据结构
在中序遍历线索树过程中,按下述两条 原则即可找到后继结点:
– 1) 如果某结点的右线索标志域为1,说明其 右指针域是线索,这个线索所指的即是该结 点的后继结点;
– 2) 如果某结点的右线索标志为0,则其右指 针域是指向右儿子结点的指针,由此结点的 右儿子结点起按左指针域指针逐结点向左查 找,一直找到左线索标志域为1的结点,即 是该结点的后继结点。
{
中
if(p!=NULL)
序
{ inthread(p->left,pre); /*左子树线索化*/
线
if(p->left==NULL)
索
/*若当前结点的左子树为空,则建立指 向其前趋结点的前趋线索*/
化
{
算
p->ltag=1;
法
p->left=pre; }
else
p->ltag=0树;
if (pre!=NULL && pre->right==NULL)
这种结点类型和相应结点的指针类型定义如 下:
typedef struct tnode {
ElemType data; int ltag,rtag; /*ltag和rtag只能取值为0或1*/ struct tnode *left,*right; }tbtree;
线索二叉树
0 A0 0 B1
0 C0
1 D0
1 E1
1F1
1 G1
(b) root
0
1
ห้องสมุดไป่ตู้
0 A0
0 B1
0 C0
0 A0
0 B1
0 C0
1 D0
1 E1
1F1
1 D0
1 E1
1F1
1 G1
1 G1
(c)
(d)
线索二叉树 b—中序 c—前序 d—后序
一旦建立了某种方式的线索二叉树后,用户程序就可以 像操作双向链表一样操作该线索二叉树。
if(tree->current == tree->root) tree->nextComplete = 1;
}
int EndOfNext(ThreadBiTree *tree) //判断是否已到中序线索二叉树的最后一个结点 { return tree->nextComplete; }
例8-3 编写一个程序,首先建立如图8-10(a)所示的不带头结点的二叉树, 然后中序线索化该二叉树,最后用循环结构输出该中序线索化二叉树各结 点的序列信息。
这种算法设计要求分别设计三个函数: First():定位在第一个结点位置; Next():移动到下一个结点位置; End():是否已经到最后下一个结点位置; 当然,还需要一个根据二叉树构造线索二叉树的函数。
typedef struct { ThreadBiNode *root;
ThreadBiNode *current; int nextComplete; }ThreadBiTree;
规定:当某结点的左指针为空时,令该指针指向按某种方法遍历二叉树时 得到的该结点的前驱结点;当某结点的右指针为空时,令该指针指向按某种 方法遍历二叉树时得到的该结点的后继结点。仅仅这样做会使我们不能区分 左指针指向的结点到底是左孩子结点还是前驱结点,右指针指向的结点到底 是右孩子结点还是后继结点。因此我们再在结点中增加两个线索标志位来区 分这两种情况。
线索二叉树
6·4 线索二叉树1、线索二叉树的结点结构二叉树的遍历本质上是将一个复杂的非线性结构转换为线性结构,使每个结点都有了唯一前驱和后继(第一个结点无前驱,最后一个结点无后继)。
对于二叉树的一个结点,查找其左右子女是方便的,其前驱后继只有在遍历中得到。
为了容易找到前驱和后继,有两种方法。
一是在结点结构中增加向前和向后的指针fwd和bkd,这种方法增加了存储开销,不可取;二是利用二叉树的空链指针。
现将二叉树的结点结构重新定义如下:其中:ltag=0 时ltag=1 时lchild指向前驱;rtag=0 时rchild指向左子女;rtag=1 时rchild指向后继;以这种结点结构构成的二叉链表作为二叉树的存储结构,叫做线索链表,指向前驱和后继的指针叫线索,加上线索的二叉树叫线索二叉树,对二叉树进行某种形式遍历使其变为线索二叉树的过程叫线索化。
学习线索化时,有三点必须注意:一是何种“序”的线索化,是先序、中序还是后序;二是要“前驱”线索化、“后继”线索化还是“全”线索化(前驱后继都要);三是只有空指针处才能加线索。
2、对二叉树进行中序线索化的算法bithptr *pre; /* 全程变量*/void INTHREAD(bithptr *p){if(p!=NULL){ INTHREAD(p->lchild); /* 左子树线索化*/if(p->lchild==NULL) { p->ltag=1;p->lchild=pre;}if(p->rchild==NULL) p->rtag=1;if(pre!=NULL && pre->rtag==1) pre->rchild=p;pre=p; /* 前驱指向当前结点*/INTHREAD(p->rchild); /* 右子树线索化*/}3、在线索二叉树上查找前驱和后继(1)中序线索二叉树:若结点的ltag=1,lchild指向其前驱;否则,该结点的前驱是以该结点为根的左子树上按中序遍历的最后一个结点。
线索二叉树
void InThreading(BiThrTree p) { if ( p ) { InThreading ( p -> lchild ); //左子树中序线索化 左子树中序线索化 if ( p->lchild = = NULL ) { p->LTag=Thread; p->lchild= pre; } //左线索为 ; 左线索为pre 左线索为 if ( pre->rchild == NULL ) { pre->RTag=Thread; pre->rchild= p ;} //后继线索 后继线索 pre = p; //保持 指向 的前驱 保持pre指向 保持 指向p的前驱 InThreading(p -> rchild ); //右子树中序线索化 右子树中序线索化 } }//InThreading
指向该线性序列中的“前驱”和 “后继” 的指针 指针,称作“线索” 线索” 指针 线索
A B C D E F G H K
^B
C^ E^
^D^
包含 “线索” 的存储 结构,称作 “线索链 线索链 表” 与其相应的二叉树, 线索二叉树” 称作 “线索二叉树 线索二叉树中序线索二叉树源自A0 1B
C
NULL
Status InOrderThreading (BiThrTree &Thrt , BiThrTree T ) { //将二叉树 改变为其中序线索二叉树 将二叉树T改变为其中序线索二叉树 if ( !Thrt = (BiThrTree ) malloc (sizeof(BiThrNode))) exit ( OVERFLOW ); Thrt-> LTag = Link ; Thrt ->RTag = Thread; Thrt -> rchild = Thrt; if ( !T ) Thrt -> lchild = Thrt; //空树 空树 else { Thrt -> lchild = T ; pre = Thrt ; InTreading( T ); //中序遍历进行中序线索化 中序遍历进行中序线索化 pre-> rchild = Thrt; pre->RTag= Thread; Thrt -> rchild = pre; } return OK; }
简述二叉树的五种形态
简述二叉树的五种形态二叉树是一种常用的数据结构,它由节点组成,每个节点最多有两个子节点。
根据节点的分布情况,二叉树可以分为五种形态,分别是满二叉树、完全二叉树、平衡二叉树、搜索二叉树和线索二叉树。
一、满二叉树满二叉树是指除了叶子节点外,每个节点都有两个子节点的二叉树。
也就是说,满二叉树的所有层都是满的,并且最后一层的叶子节点都靠左排列。
满二叉树的节点数可以通过公式计算得到,假设树的高度为h,则节点数为2^h - 1。
满二叉树的特点是结构简单,查找速度快。
在满二叉树中,任意两个节点的路径长度都相同。
二、完全二叉树完全二叉树是指除了最后一层之外,其他层都是满的,并且最后一层的叶子节点都靠左排列的二叉树。
完全二叉树的特点是节点数较少,结构相对简单。
完全二叉树通常用数组来表示,因为它的节点之间的关系可以通过数组的下标来表示。
在完全二叉树中,任意一个节点的左子节点的下标为2i,右子节点的下标为2i+1。
三、平衡二叉树平衡二叉树是指左右子树的高度差不超过1的二叉树。
平衡二叉树的特点是查找、插入和删除的时间复杂度都为O(logn),其中n是节点的数量。
平衡二叉树的高度可以通过节点的平衡因子来计算,平衡因子定义为左子树的高度减去右子树的高度。
平衡因子的取值范围为-1、0和1,当平衡因子的绝对值大于1时,需要通过旋转操作来调整树的平衡性。
四、搜索二叉树搜索二叉树,也称为二叉搜索树或排序二叉树,是一种特殊的二叉树。
它的特点是对于树中的任意一个节点,其左子树中的所有节点都小于它,右子树中的所有节点都大于它。
搜索二叉树的中序遍历结果是一个递增的有序序列。
搜索二叉树的特点是可以快速地查找某个节点,时间复杂度为O(logn),其中n是节点的数量。
但是,如果搜索二叉树不平衡,即左子树或右子树过深,则会导致查找的时间复杂度退化为O(n)。
五、线索二叉树线索二叉树是对二叉树进行了优化的数据结构,它通过添加指向前驱和后继节点的线索,使得遍历操作更加高效。
数据结构C语言版_线索二叉树
int InOrderTraverse_Thr(BiThrTree T,int(*Visit)(TElemType))
{
BiThrTree p;
p=T->lchild; // p指向根结点
while(p!=T)
{ // 空树或遍历结束时,p==T
// 空格(字符型)表示空结点
int CreateBiThrTree(BiThrTree *T)
{
TElemType h;
scanf("%c",&h);
if(h==Nil)
*T=NULL;
else
{
*T=(BiThrTree)malloc(sizeof(BiThrNode));
if(!p->lchild) // 没有左孩子
{ Biblioteka p->LTag=Thread; // 前驱线索
p->lchild=pre; // 左孩子指针指向前驱
}
if(!pre->rchild) // 前驱没有右孩子
{
pre->RTag=Thread; // 后继线索
"b为左子树的二叉树)\n");
CreateBiThrTree(&T); // 按先序产生二叉树
InOrderThreading(&H,T); // 中序遍历,并中序线索化二叉树
printf("中序遍历(输出)二叉线索树:\n");
InOrderTraverse_Thr(H,vi); // 中序遍历(输出)二叉线索树
线索化二叉树详解
线索化⼆叉树详解线索化⼆叉树详解说明1. 线索化⼆叉树,由字⾯意思,就是将⼆叉树的节点拿线索连接起来2. 实质上,也就是将⼆叉树的叶⼦节点左右指针域彼此连接⼀个节点3. ⼆叉树的⾮叶⼦节点的左右指针域都各⾃连接了⼀个节点,但是叶⼦节点的左右指针域是空的,因此考虑将叶⼦节点的左右指针域按照某种遍历次序连接起来4. 按照⼆叉树的遍历⽅式,有前序中序后续三种遍历⽅式,因此可以形成三种链式结构5. 每个叶⼦节点前⼀个节点称为前驱节点,后⼀个节点称为后继节点,如果当前节点没有前驱或者后继节点,则直接置为空6. 以中序线索化⼆叉树为例,编写中序线索化⼆叉树的⽅法7. 先判断当前节点是否为空,如果为空,则直接返回8. 否则先向左递归线索化⼆叉树的左⼦树9. 然后再线索化当前节点,定义属性pre保存当前节点的前⼀个节点,因此当前节点的前⼀个节点置为pre10. 注意当前节点的后⼀个节点,需要⽤pre保存当前节点,然后遍历到后⼀个节点,然后⽤pre指向11. 注意第⼀个节点和最后⼀个节点12. 中序线索化如下,前序和后续类似源码及分析节点类//创建节点class HeroNode{//编号private int no;//姓名private String name;//左⼦树private HeroNode left;//右⼦树private HeroNode right;//线索化的前驱节点类型,是节点还是树,假定 0 为树, 1 为节点private int leftType;//线索化的后继节点类型private int rightType;public int getLeftType() {return leftType;}public void setLeftType(int leftType) {this.leftType = leftType;}public int getRightType() {return rightType;}public void setRightType(int rightType) {this.rightType = rightType;}//构造器,左⼦树和右⼦树默认为空public HeroNode(int no, String name) {this.no = no; = name;}public int getNo() {return no;}public void setNo(int no) {this.no = no;}public String getName() {return name;}public void setName(String name) { = name;}public HeroNode getLeft() {return left;}public void setLeft(HeroNode left) {this.left = left;}public HeroNode getRight() {return right;}public void setRight(HeroNode right) {this.right = right;}@Overridepublic String toString() {return "HeroNode{" +"no=" + no +", name='" + name + '\'' +'}';}//删除节点/**** @param no 要删除的节点编号*/public void delNode(int no){//判断当前节点的左⼦树是否为空,如果不为空,再判断是否为要删除的节点if (this.left != null && this.left.no == no){this.left = null;}//判断当前节点的右⼦树是否为空,如果不为空,再判断是否为要删除的节点if (this.right != null && this.right.no == no){this.right = null;}//否则向左向右递归if (this.left != null){this.left.delNode(no);}if (this.right != null){this.right.delNode(no);}}//前序中序后序遍历主要区别在于⽗节点的输出位置不同,/*** 前序遍历先输出⽗节点信息,然后判断左⼦树是否为空,如果不为空,则递归前序遍历 * 然后再判断右⼦树是否为空,如果不为空,则递归遍历前序遍历*///前序遍历public void preOrder(){//先输⼊当前节点信息System.out.println(this);//然后判断当前节点的左⼦树是否为空if (this.left != null){this.left.preOrder();}//再判断当前节点的右⼦树是否为空if (this.right != null){this.right.preOrder();}}//中序遍历public void infixOrder(){//先判断当前节点的左⼦树是否为空if (this.left != null){this.left.infixOrder();}//再输出当前节点的信息System.out.println(this);//然后再判断当前节点的右⼦树是否为空if (this.right != null){this.right.infixOrder();}}//后序遍历public void postOrder(){//先判断当前节点的左⼦树是否为空if (this.left != null){this.left.postOrder();}//再判断当前节点的右⼦树是否为空if (this.right != null){this.right.postOrder();}//最后输出当前节点的信息System.out.println(this);}//前序查找/*** 前序遍历查找* @param no 要查找的节点编号* @return 返回查找的结果*/public HeroNode preOrderSearch(int no){//先判断当前节点是不是要查找的节点if (this.no == no){return this;}//如果当前节点不是要查找的节点,则判断左⼦树是否为空,若不为空,则递归前序查找 HeroNode resNode = null;if (this.left != null){resNode = this.left.preOrderSearch(no);}//如果在左⼦树找到,则直接返回if (resNode != null){return resNode;}//如果左⼦树也没有找到,则判断右⼦树是否为空,并递归if (this.right != null){resNode = this.right.preOrderSearch(no);}return resNode;}//中序查找/*** 中序遍历查找* @param no 要查找的节点编号* @return 返回查找的结果*/public HeroNode infixOrderSearch(int no){//先判断当前节点左⼦树是否为空,如果不为空则递归中序查找//定义变量保存查找的结果HeroNode resNode = null;if (this.left != null){resNode = this.left.preOrderSearch(no);}//如果查找到,则直接返回if (resNode != null){return resNode;}//如果没有找到,判断当前节点是不是要查找的节点if (this.no == no){return this;}//如果还没有找到,则判断右⼦树是否为空,不为空则递归中序查找if (this.right != null){resNode = this.right.infixOrderSearch(no);}return resNode;}//后序查找/*** 后续遍历查找* @param no 要查找的节点编号* @return 返回查找的结果*/public HeroNode postOrderSearch(int no){//判断当前节点的左⼦树是否为空,如果不为空,则递归后续查找HeroNode resNode = null;if (this.left != null){resNode = this.left.postOrderSearch(no);}if (resNode != null){return resNode;}if (this.right != null){resNode = this.right.postOrderSearch(no);}if (resNode != null){return resNode;}if (this.no == no){return this;}return resNode;}}线索化⼆叉树类//创建⼀颗线索化⼆叉树class ThreaderBinaryTree{//⼆叉树必有根节点private HeroNode root;//定义变量指向前驱节点,默认为空private HeroNode pre = null;public void setRoot(HeroNode root) {this.root = root;}//编写中序线索化⼆叉树的⽅法/**** @param node node为当前要中序线索化的节点*/public void infixThreadedBinaryTree(HeroNode node){//先判断当前节点是否为空if (node == null){return;}//如果不为空,先线索化左⼦树infixThreadedBinaryTree(node.getLeft());//再线索化当前节点//当前节点的前驱节点为pre,后继节点需要在下⼀个节点连通,因为是单向的 //设置当前节点的前驱节点,并设置前驱节点类型if (node.getLeft() == null){node.setLeft(pre);node.setLeftType(1);}//设置当前节点的后继节点及其类型if (pre != null && pre.getRight() == null){pre.setRight(node);pre.setRightType(1);}//让pre指向当前节点pre = node;//最后再线索化右⼦树infixThreadedBinaryTree(node.getRight());}//重载线索化的⽅法public void infixThreadedBinaryTree(){this.infixThreadedBinaryTree(root);}//删除节点/**** @param no 要删除的节点编号*/public void delNode(int no){//先判断⼆叉树是否为空if (this.root != null){//再判断当前root节点是不是要删除的节点if (this.root.getNo() == no){root = null;}else {this.root.delNode(no);}}else {System.out.println("⼆叉树为空,不能删除...");}}//前序遍历public void preOrder(){if (this.root != null){this.root.preOrder();}else {System.out.println("⼆叉树为空...");}}//中序遍历public void infixOrder(){if (this.root != null){this.root.infixOrder();}else {System.out.println("⼆叉树为空...");}}//后续遍历public void postOrder(){if (this.root != null){this.root.postOrder();}else {System.out.println("⼆叉树为空...");}}//前序查找public HeroNode preOrderSearch(int no){if (this.root != null){return this.root.preOrderSearch(no);}else {return null;}}//中序查找public HeroNode infixOrderSearch(int no){if (this.root != null){return this.root.infixOrderSearch(no);}else {return null;}}//后续查找public HeroNode postOrderSearch(int no){if (this.root != null){return this.root.postOrderSearch(no);}else {return null;}}}测试类public static void main(String[] args) {ThreaderBinaryTree threaderBinaryTree = new ThreaderBinaryTree(); HeroNode root = new HeroNode(1,"tom");HeroNode node2 = new HeroNode(3,"jack");HeroNode node3 = new HeroNode(6,"smith");HeroNode node4 = new HeroNode(8,"king");HeroNode node5 = new HeroNode(10,"mary");HeroNode node6 = new HeroNode(14,"dop");root.setLeft(node2);root.setRight(node3);node2.setLeft(node4);node2.setRight(node5);node3.setLeft(node6);threaderBinaryTree.setRoot(root);//进⾏线索化threaderBinaryTree.infixThreadedBinaryTree();//测试线索化的结果System.out.println("node5的前⼀个节点 = " + node5.getLeft());System.out.println("node5的后⼀个节点 = " + node5.getRight());}。
线索二叉树(图)
线索二叉树:遍历二叉树:实际上是对二叉树(非线性结构)进行的线性化操作,即以一定规则将二叉树中的结点排列成一个线性序列(先序序列、中序序列和后序序列)。
举例:图6.9所示的二叉树中的结点,按中序遍历可得到中序序列:a+b*c-d-e/f,其中‘c’的前驱为‘*’,后继为‘-’。
当以二叉链表作为二叉树的存储结构时,只能找到结点的左右孩子信息,而不能直接得到结点在任一线性序列中的前驱和后继信息,因为这种信息只有在遍历的动态过程中才能得到。
如何保存这种在遍历过程中得到的结点的前驱和后继信息呢?方法一:在二叉链表的每个结点上增加两个指针域fwd和bkwd,分别指向在依任一次序遍历时得到的前驱和后继信息。
(大大影响存储密度)方法二:利用二叉链表中的空链域来存放结点的前驱和后继信息。
(在有n个结点的二叉链表中必定存在n+1个空链域!)(不影响存储密度)为此,可以将二叉链表中的结点结构作如下修改:lchild LTag data RTag rchild其中:Ltag = 0 lchild域指示结点的左孩子1 lchild域指示结点的前驱Rtag = 0 rchild域指示结点的右孩子1 rchild域指示结点的后继我们把如此修改后的二叉链表称为二叉树的线索链表,其中指向结点前驱和后继的指针称为线索。
相应地,把添加线索后的二叉树称为线索二叉树(Threaded Binary Tree)。
对二叉树以某种次序遍历使其变为线索二叉树的过程叫做线索化。
举例:图6.11(a)所示为中序线索二叉树,与其对应的中序线索链表如图 6.11(b)所示。
其中实线为指针(指向左、右子树),虚线为线索(指向前驱和后继)。
在线索树上进行遍历,只要找到序列中的第一个结点,然后依次找结点的后继直到其后继为空时而停止。
关键是如何在线索树中找结点的后继?二叉树的二叉线索存储表示:(p133-134)线索二叉树的遍历:(以中序线索二叉树为例,即中序遍历二叉线索树)算法6.5二叉树的线索化:(以中序线索化为例,即通过中序遍历建立中序线索链表)算法6.6,算法6.7。
数据结构——用C语言描述(第3版)教学课件第6章 树与二叉树
6.2 二叉树 6.2.1 二叉树的定义与基本操作 6.2.2 二叉树的性质 6.2.3 二叉树的存储结构
6.2.1 二叉树的定义与基本操作 定义:我们把满足以下两个条件的树型结构叫做二 叉树(Binary Tree): (1)每个结点的度都不大于2; (2)每个结点的孩子结点次序不能任意颠倒。
有序树:在树T中,如果各子树Ti之间是有先后次序的,则称为有序树。 森林:m(m≥0)棵互不相交的树的集合。将一棵非空树的根结点删去,树就变成一 个森林;反之,给森林增加一个统一的根结点,森林就变成一棵树。
同构:对两棵树,通过对结点适当地重命名,就可以使两棵树完全相等(结点对应相 等,对应结点的相关关系也像等),则称这两棵树同构。
二叉树的基本结构由根结点、左子树和右子树组成
如图示
LChild Data RChild
Data
LChild RChild
用L、D、R分别表示遍历左子树、访问根结点、遍 历右子树,那么对二叉树的遍历顺序就可以有:
(1) 访问根,遍历左子树,遍历右子树(记做DLR)。 (2) 访问根,遍历右子树,遍历左子树(记做DRL)。 (3) 遍历左子树,访问根,遍历右子树(记做LDR)。 (4) 遍历左子树,遍历右子树,访问根 (记做LRD)。 (5) 遍历右子树,访问根,遍历左子树 (记做RDL)。 (6) 遍历右子树,遍历左子树,访问根 (记做RLD)。
(8) NextSibling(Tree,x): 树Tree存在,x是Tree中的某个结点。若x不 是其双亲的最后一个孩子结点,则返回x后面的下一个兄弟结点,否则 返回“空”。
基本操作:
(9) InsertChild(Tree,p,Child): 树Tree存在,p指向Tree 中某个结点,非空树Child与Tree不相交。将Child插入Tree中, 做p所指向结点的子树。
浅谈线索二叉树
浅谈线索二叉树摘要:数据结构中二叉树有很多的遍历算法,但本质都是将属性结构转换为线性序列,简化问题。
在遍历序列中,每个节点都有自己的前驱和后去,但在二叉树遍历过程中寻求答案却因为时间复杂度等因素使操作效率低下。
线索二叉树很好地解决了这一问题,本文是在二叉树的基础上加入线索二又树实现数据的快速可操作性。
关键词:数据结构;线索二叉树;应用前言为了实现在遍历序列中快速查找节点的前驱、后继,利用二叉链表中空的指针域,指向结点在遍历序列中的前驱、后继,这些指向前驱、后继的指针就是线索。
1、数据结构数据结构起源于程序设计,主要是电脑中数据的组织方式、存储结构和处理方法。
在程序的编程中,写一个“好”的程序,就是要选择一个合理的数据结构和算法。
数据的逻辑结构常用结构图来描述,将每一个数据元素看做一个结点,在计算处理数据的时候,算法同样很关键。
一种算法的有穷性,必须对任何合法的输入在执行有穷之后结束,并且每 1 步都在有穷时间内完成。
树形结构就是用于有层次关系的数据表示,表达大多数问题求解的思路,而二叉树的结点数和深度,为二叉树存储结构的选择提供了预备知识和思考线索。
线索二又树借助二又树遍历的程序框架建立,通过函数将整个二叉树的线索化分解,求后继、前驱结点的算法增加数据操作的效率,缩短时间,提高数据的操作性。
2、树的结构定义树形结构是信息的重要组织形式之一,树是一种数据结构,它是由n(n>=1)个有限结点组成一个具有层次关系的集合。
把它叫做“树”是因为它看起来像1棵倒挂的树,也就是说它是根朝上,而叶朝下的。
它具有以下的特点:(1)每个结点有零个或多个子结点;(2)每一个子结点只有一个父结点;(3)没有前驱的结点为根结点;(4)除了根结点外,每个子结点可以分为多个不相交的子树。
树的应用非常广泛,在程序设计中实现通用树的基本功能需要考虑很多功能,比方内存分配,添加节点,修改节点,删除节点,移动节点,遍历,查询,保存,读取问题。
数据结构 第六章 树和二叉树
F
G
H
M
I
J
结点F,G为堂兄弟 结点A是结点F,G的祖先
5
树的基本操作
树的应用很广,应用不同基本操作也不同。下面列举了树的一些基本操作: 1)InitTree(&T); 2)DestroyTree(&T); 3)CreateTree(&T, definition); 4)ClearTree(&T); 5)TreeEmpty(T); 6)TreeDepth(T); 7) Root(T); 8) Value(T, &cur_e); 9) Assign(T, cur_e, value); 10)Paret(T, cur_e); 11)LeftChild(T, cur_e); 12)RightSibling(T, cur_e); 13)InsertChild(&T, &p, i, c); 14)DeleteChild(&T,&p, i); 15)TraverseTree(T, Visit( ));
1
2 4 8 9 10 5 11 12 6 13 14 3 7 15 4 6 2
1
3
5 7
证明:设二叉树中度为1的结点个数为n1 根据二叉树的定义可知,该二叉树的结点数n=n0+n1+n2
又因为在二叉树中,度为0的结点没有孩子,度为1的结点有1 个孩子,度为2的结点有2个结孩子,故该二叉树的孩子结点 数为 n0*0+n1*1+n2*2(分支数) 而一棵二叉树中,除根结点外所有都为孩子结点,故该二叉 树的结点数应为孩子结点数加1即:n=n0*0+n1*1+n2*2+1
文件夹1
文件夹n
拓展阅读6——线索二叉树的操作
线索二叉树的操作*线索二叉树的主要操作是建立线索二叉树和遍历线索二叉树。
下面以中序线索二叉树为例介绍线索二叉树的建立和遍历,其他两种线索二叉树与此类似。
1.中序线索二叉树的建立线索二叉树的建立是基于对二叉树进行遍历的基础上完成的,与前面讲的二叉树遍历的应用所不同的是,在对二叉树进行遍历时所做的操作是将结点的空指针改为指向其前驱或后继结点。
线索二叉树的建立分成两部分来完成:一部分是建立线索链表的头结点,另一部分是对已有的二叉链表进行线索化。
对二叉链表线索化也是采用递归的形式,先对左子树进行线索化,然后对根结点线索化,最后对右子树进行线索化。
在对根结点进行线索化时,需要两个指针p和pre,其中p 指向当前根结点,pre指向其前驱结点。
若当前根结点的lchild指针为空,则将其指向其前驱结点,同时其标志值改为1,否则不做修改。
若其前驱结点的rchild指针为空,则将其指向其后继结点,即指针p所指的结点,同时其标志值改为1,否则不做修改。
对二叉链表线索化的算法如下:【算法1】:void InOrderThread(PTBNode p,PTBNode *pre)/*二叉树的中序线索化*/{if(p!=NULL){InOrderThread(p->lchild,pre); /*中序线索化左子树*/if(p->lchild==NULL) /*当前结点没有左子树,将其lchild指针指向前驱结点*/{p->lchild=*pre;p->ltag=1;}if((*pre)->rchild==NULL)/*前驱结点没有右子树,将其rchild指针指向后继结点*/{(*pre)->rtag=1;(*pre)->rchild=p;}*pre=p;InOrderThread(p->rchild,pre); /* 中序线索化右子树*/}}建立线索链表的头结点时,首先创建头结点,然后让头结点的lchild指针指向二叉链表的根结点,置ltag为0;让头结点的rchild指针指向遍历序列中最后一个结点,置rtag为1。
线索二叉树的应用资料
课程设计说明书(数据结构课程设计)专业:网络工程课程名称: 数据结构课程设计班级: 网络B11-1设计题目: 线索二叉树的应用设计时间: 2013-2-25 至2013-3-8评语:_____________________________________________________________________________________________________________________________________________________________________________________________________评阅成绩:____评阅教师:__一、问题描述与需求分析1、问题描述本实验的问题是建立一个线索二叉树,并实现线索二叉树的插入、删除、恢复线索等功能。
2、功能需求分析本程序要实现的功能是: 1. 线索二叉树的建立。
2.线索二叉树的插入。
3.线索二叉树的删除。
4.线索二叉树的恢复。
想要完成上面的功能,我们首先是要知道上面是线索二叉树。
我们可以从数据结构的书上找到答案,利用二叉链表的空指针域将空的左孩子指针域改为指向其前驱,空的右孩子指针域改为指向其后继。
这种改变指向的指针称为线索,加上了线索的二叉链表称为线索链表。
N个结点的二叉链表中含有n+1个空指针域。
利用二叉链表中的空指针域,存放指向结点的在某种遍历次序下的前驱和后继结点的指针,这种加上了线索的二叉链表称为线索链表。
相应的二叉树称为线线索二叉树。
根据线索二叉树性质的不同,线索二叉树可以分为前序线索二叉树,中序线索二叉树和后续线索二叉树三种,此次课程设计中使用的是中序线索二叉树。
二、概要设计1、总体设计思路首先就是要建立一个二叉树,然后再对二叉树进行线索化。
线索链表中的结点结构为:其中:线索二叉树及其存储结构如在线索树上进行遍历,只需先找到序列中的第一个结点,然后依次找结点后继为空时而止。
数据结构详细教案——树与二叉树
数据结构教案第六章树与二叉树目录6.1树的定义和基本术语 (1)6.2二叉树 (2)6.2.1 二叉树的定义 (2)6.2.2 二叉树的性质 (4)6.2.3 二叉树的存储结构 (5)6.3树和森林 (6)6.4二叉树的先|中|后序遍历算法 (7)6.5先|后|中序遍历的应用扩展 (9)6.5.1 基于先序遍历的二叉树(二叉链)的创建 (9)6.5.2 统计二叉树中叶子结点的数目 (9)6.5.3 求二叉树的高度 (10)6.5.4 释放二叉树的所有结点空间 (11)6.5.5 删除并释放二叉树中以元素值为x的结点作为根的各子树 (12)6.5.6 求位于二叉树先序序列中第k个位置的结点的值 (12)6.5.7 线索二叉树 (13)6.5.8 树和森林的遍历 (14)6.6二叉树的层次遍历 (16)6.7判断一棵二叉树是否为完全二叉树 (16)6.8哈夫曼树及其应用 (18)6.8.1 最优二叉树(哈夫曼树) (18)6.8.2 哈夫曼编码 (19)6.9遍历二叉树的非递归算法 (19)6.9.1 先序非递归算法 (19)6.9.2 中序非递归算法 (20)6.9.3 后序非递归算法 (21)第6章二叉树和树6.1 树的定义和基本术语1、树的递归定义1)结点数n=0时,是空树2)结点数n>0时有且仅有一个根结点、m个互不相交的有限结点集——m棵子树2、基本术语结点:叶子(终端结点)、根、内部结点(非终端结点、分支结点);树的规模:结点的度、树的度、结点的层次、树的高度(深度)结点间的关系:双亲(1)—孩子(m),祖先—子孙,兄弟,堂兄弟兄弟间是否存在次序:无序树、有序树去掉根结点非空树森林引入一个根结点3、树的抽象数据类型定义树特有的操作:查找:双亲、最左的孩子、右兄弟结点的度不定,给出这两种操作可以查找到一个结点的全部孩子插入、删除:孩子遍历:存在一对多的关系,给出一种有规律的方法遍历(有且仅访问一次)树中的结点ADT Tree{数据对象:D={a i | a i∈ElemSet, i=1,2,…,n, n≥0}数据关系:若D为空集,则称为空树;若D仅含一个数据元素,则R为空集,否则R={H},H是如下二元关系:(1) 在D中存在唯一的称为根的数据元素root,它在关系H下无前驱;(2) 若D-{root}≠Ф,则存在D-{root}的一个划分D1, D2, …, D m (m>0)(D i 表示构成第i棵子树的结点集),对任意j≠k (1≤j, k≤m) 有D j∩D k=Ф,且对任意的i (1≤i≤m),唯一存在数据元素x i∈D i, 有<root,x i>∈H(H表示结点之间的父子关系);(3) 对应于D-{root}的划分,H-{<root, x1>,…, <root, x m>}有唯一的一个划分H1, H2, …, H m(m>0)(H i表示第i棵子树中的父子关系),对任意j≠k(1≤j,k≤m)有H j∩H k=Ф,且对任意i(1≤i≤m),H i是D i上的二元关系,(D i, {H i})是一棵符合本定义的树,称为根root的子树。
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
二叉树的线索化:
以二叉链表作为存储结构时,只能找到结点的左、右孩子信
息,而不能直接得到结点在任一序列(先序、中序或后序序列)
中的前驱和后继信息,这种信息只有在遍历的动态过程中才能得
到。
为了保存这种在遍历过程中得到的信
息,我们利用二叉链表中的空链域(由于结点没有左子树或右子树),
来存放结点的前驱和后继信息。
作如下规定:
①若结点有左子树,则其lchild 域指示其左孩子,否则令
lchild 域指示其前驱;
②若结点有右子树,则其rchild 域指示其右孩子,否则令
rchild 域指示其后继。
(1)线索链表的结点结构
lchild LTag data RTag rchild
其中: data:数据域;
lchild :左指针域,指向该结点的左孩子;
rchild :右指针域,指向该结点的右孩子;
0lchild域指示结点的左孩子
LTag =
1lchild域指示结点的前驱
0rchild域指示结点的右孩子
RTag =
1rchild域指示结点的后继
请将根据图 1 所示编写一个程序实现构建线索二叉树。
thrt
0 1
bt
0 A 0
0 B 0 0 C 0
1 D 1 0 E 0 1 F 1 1 G 1
1 H 1 0 I 0
1 J 1 1 k 1
图1
#include<stdio.h>
#include<malloc.h>
#include<stdlib.h>
#define NULL 0
#define OK 1
#define ERROR 0
typedef enum PointerTag { Link,Thread };//Link==0, 指向孩子; Thread==1, 指向前驱后继 typedef char TElemType;
typedef int Status;
//线索化二叉树的存储结构
typedef struct BiThrNode
{ TElemType data;
struct BiThrNode *lchild,*rchild; // 左孩子与右孩子的指针
PointerTag LTag,RTag; //左右标志域,指示是孩子还是前驱后继
} BiThrNode,*BiThrTree;
//按照先序输入二叉树各个节点,创建原
始二叉树, Status CreateBiTree(BiThrTree&
T) {
#表示空树
char ch;
scanf("%c",&ch);
if('#'==ch) T=NULL;
else
{ T=(BiThrTree )malloc(sizeof(BiThrNode));
if(!T) exit(ERROR);
T->data=ch;
T->LTag=T->RTag=Link;// 建表时初始化都为
CreateBiTree(T->lchild);// 构造左子树CreateBiTree(T->rchild);// 构造右子树Link(
即
0)
}
return OK;
}
BiThrTree pre=NULL; // 定义 pre 为函数 InThreading 的外部变量,使其指向最后一个节点 void InThreading(BiThrTree p); // 函数声明
//中序遍历二叉树,将其线索化,Thrt 指向头结点
Status InOrderThreading(BiThrTree &Thrt,BiThrTree T)
{
Thrt=(BiThrTree)malloc(sizeof(BiThrNode)); // 分配头结点if(!Thrt) exit(ERROR);
//以下三步都是在初始化头结点
Thrt->LTag=Link;// 假设头结点的有右孩子
Thrt->RTag=Thread;// 假设头结点有后继
Thrt->rchild=Thrt;// 暂时使头结点的右指针指向自己
if(!T) Thrt->lchild=Thrt; // 如果树空,就令头结点左指针指向自己
else
{ // 下面先线索头结点
Thrt->lchild=T; // 头结点左指针指向根
pre=Thrt; // 先 pre 指向头指针(也就是根节点的前驱)
//接着线索二叉树
InThreading(T); // 调用函数将T 线索化
//最后线索尾节点
pre->RTag=Thread;// 假设最后一点有后继
pre->rchild=Thrt;// 最后一个点右指针指向头结点
Thrt->rchild=pre;// 头结点的右指针指向最后一个点
}
return OK;
}
//将根为p 的二叉树线索化,千万记住,p 是局部变量,当进入下一次递归时他会屏蔽上一
个p,
//但是上一个p 仍然保留着,等到函数返回时他就又
恢复了上一个
void InThreading(BiThrTree p)
{
p
if(p) // 仅仅当 p 不空时才后继续下面的操作,如果
{ InThreading(p->lchild); // 左子树线索化p 空,那么直接结束,不返回任何值
// 对于当前节点,仅仅处理他与前驱的关系
if(!p->lchild) { p->LTag=Thread; p->lchild=pre; }// 如果当前点左边为空,
指向前驱 if(!pre->rchild) { pre->RTag=Thread; pre->rchild=p; } // 如果上
一点右空,指向后继
pre=p;
InThreading(p->rchild); // 右子树线索化
}
}
//非递归调用,中序线索二叉树 ,T 指向头结点,头结点的 lchild
指向根 Status InOrderTraverse_Thr(BiThrTree T,Status
(*Visit)(TElemType e))
{
BiThrTree p;
p=T->lchild; //p 指向根节点
while(p!=T)
{
while(p->LTag==Link) p=p->lchild; // 一直到最左一个元素
Visit(p->data); // 访问最左
while(p->RTag==Thread&&p->rchild!=T)
{ p=p->rchild;Visit(p->data); } // 如果有后继就访问后继
p=p->rchild; // 没有后继先指向右子树,下一个循环会找到最左子孙
}
return OK;
}
Status Print(TElemType e)
{
printf("%c ",e);
return OK;
}
int main()
{
BiThrTree t=NULL,Thrt=NULL; CreateBiTree(t);// 创建树InOrderThreading(Thrt,t);
InOrderTraverse_Thr(Thrt,Print); return 0;
}。