一提到线索化二叉树,可能有些小伙伴的头都要炸了!如果你遇到了问题,可以参照一下这个版本,希望对你有帮助。
内容:
1、二叉树的建立
2、二叉树的中序线索化
3、二叉树的线索化遍历
4、前继和后继结点的查找
5、完整代码
6、测试
结构:
#include<stdio.h>
#include<stdlib.h>
typedef char Elemtype;
typedef enum {
Link,Thread //枚举型,Link默认值为0,Thread默认值为1
}PointerTag;
typedef struct _BiTree{
struct _BiTree *lchild,*rchild,*parent;
Elemtype data;
PointerTag LTag,RTag;
}BiTree;
1、二叉树的建立:
//先序创建线索二叉树,初始化双亲地址,LTag、RTag
BiTree *Create(BiTree *T,BiTree *p) //p为给parent赋值的指针,初始为NULL
{
Elemtype i;
scanf("%c",&i);
if(i==' ') T=NULL;
else
{
T=(BiTree *)malloc(sizeof(BiTree));
if(!T) return ;
T->data=i;
T->parent=p;
p=T;
T->LTag=Link; //重要的两步,如果不初始化值后面就会出错
T->RTag=Link;
T->lchild = Create(T->lchild,p);
T->rchild = Create(T->rchild,p);
}
return T;
}
注意!注意!注意!在二叉树的建立过程中一定要注意,将所有结点的LTag与RTag的初始值赋Link,也就是0
2、二叉树的中序线索化
//主线索化函数调用的辅助递归线索化函数
BiTree *InThreading(BiTree *T,BiTree *pre)
{
if(T) //重中之重,递归条件T必须存在
{
pre=InThreading(T->lchild,pre); //线索化左孩子
//左孩子不存在Ltag就等于Thread,左孩子指向前驱线索pre
if(!T->lchild) {T->LTag = Thread; T->lchild = pre;}
//前驱线索pre不存在RTag就等于Thread,右孩子就等于该层的T
if(!pre->rchild) {pre->RTag = Thread; pre->rchild = T;}
//pre随着T做线性移动
pre = T;
pre=InThreading(T->rchild,pre); //线索化左孩子
}
return pre; //返回pre的值
}
//主线索化函数,主要开辟头结点、调用辅助递归线索化函数、处理头结点及最后一个结点
BiTree *InOrderThreading(BiTree *T)
{
//开辟头结点(LTag=Link,lchild指向树根RTag=Thread,rchild指向自身)
//定义一个前驱指针pre
BiTree *pre,*Thrt = (BiTree *)malloc(sizeof(BiTree));
if(!Thrt) printf("1失败");
//头结点lchild指向树,rchild回指
Thrt->LTag = Link; Thrt->RTag = Thread; Thrt->rchild = Thrt;
if(!T) Thrt->lchild = Thrt;
else
{
Thrt->lchild = T; pre = Thrt; //pre指向头结点
pre=InThreading(T,pre); //线索化
//线索化树的最后一个结点
pre->RTag = Thread; pre->rchild = Thrt;
Thrt->rchild = pre;
}
return Thrt; //返回头结点地址
}
就这两个函数就可以实现中序二叉树的线索化,可以叫这两个函数为父子函数,由主函数调用父函数,再由父函数调用子函数进行递归线索化。
父函数宏观上的作用:
1、开辟一个头结点并加以处理。给这个头结点的左、右标志赋值,让左指针域指向二叉树(如果二叉树存在)右指针域指向自身。
2、调用子函数进行递归线索化
3、处理二叉树的最后一个结点
4、返回这个开辟的头结点的地址
子函数宏观上的作用:
1、线索化左指针域
2、判断左孩子是否存在,若不存在就让LTag=Thread,然后连接前驱结点
3、判断前驱结点的右孩子是否存在,若不存在就让RTag=Thread,然后连接当前的T值
4、让pre前驱指针跟随T移动
5、线索化右指针域
3、二叉树的线索化遍历
//打印函数
void Print_1(BiTree *T)
{
printf("%d<%c>%d\n",T->LTag,T->data,T->RTag);
}
//非递归遍历线索二叉树,调用打印函数
void InOrderTraverseThread(BiTree *T,void (*Visit)(BiTree *T))
{
BiTree *p=T->lchild;
while(p != T) //大前提,最后一个结点走完时p=T,走完了一圈
{
while(p->LTag == Link) p=p->lchild; //一直向左
Print_1(p); //打印
while(p->RTag==Thread && p->rchild!=T) //RTag=Thread且不是最后一个结点时
{
p=p->rchild; Print_1(p);
}
p=p->rchild;
}
}
因为二叉树的线索化已经完成,所以这个函数接受的是头结点。这个线索二叉树构成了一个环状的线性结构,可以从头结点开始然后遍历整个二叉树,最后回到头结点。
4、前继和后继结点的查找
//判断数据是否相等,相等则返回该地址
BiTree *Panduan(BiTree *T)
{
if(T->data == ch) return T;
else return NULL;
}
//非递归遍历线索二叉树,查找期望键值的地址
BiTree *InOrder_Search(BiTree *T,BiTree *(*Visit)(BiTree *T))
{
BiTree *p=T->lchild;
while(p != T) //大前提,最后一个结点走完时p=T,走完了一圈
{
while(p->LTag == Link) p=p->lchild; //一直向左
if(Panduan(p)) return p; //判断,如果返回值存在就返回这个返回值
while(p->RTag==Thread && p->rchild!=T) //RTag=Thread且不是最后一个结点时
{
//判断,如果返回值存在就返回这个返回值
p=p->rchild; if(Panduan(p)) return p;
}
p=p->rchild;
}
}
//查找中序遍历指定结点的后继
void Search_post(BiTree *p) //p为目标结点的地址
{
BiTree *q,*temp;
if(p->RTag == Thread) temp = p->rchild; //如果右标志为线索,则线索指向的就是后继
if(p->RTag == Link) //右标志为结点,则向右一个,再左到底为后继
{
q=p->rchild;
while(q->LTag == Link) q = q->lchild;
temp = q;
}
printf("%c的后继是%c",p->data,temp->data);
}
//查找中序遍历指定结点的前继
void Search_pre(BiTree *p) //p为目标结点的地址
{
BiTree *q,*temp;
if(p->LTag == Thread) temp=p->lchild;//若左标志为线索,则线索就指向前继
if(p->LTag == Link) //若左标志为结点,则向左一个,再右到底为前继
{
q=p->lchild;
while(q->RTag == Link) q=q->rchild;
temp = q;
}
printf("%c的前继是%c",p->data,temp->data);
}
开头两个函数是通过遍历整个线索二叉树找到想查询键值的地址,然后把这个地址传入Search_post或Search_pre中,去找到后继或前继。
Panduan()函数中的ch是个全局字符变量可改变想要查询的键值。
5、完整代码
#include<stdio.h>
#include<stdlib.h>
typedef char Elemtype;
typedef enum {
Link,Thread
}PointerTag;
typedef struct _BiTree{
struct _BiTree *lchild,*rchild,*parent;
Elemtype data;
PointerTag LTag,RTag;
}BiTree;
Elemtype ch; //全局变量,用于查找,来找到这个键值的地址
//打印数据
void Print_2(Elemtype e)
{
printf("%c",e);
}
//打印函数
void Print_1(BiTree *T)
{
printf("%d<%c>%d\n",T->LTag,T->data,T->RTag);
}
//非递归遍历线索二叉树,调用打印函数
void InOrderTraverseThread(BiTree *T,void (*Visit)(BiTree *T))
{
//一直向左
BiTree *p=T->lchild;
while(p != T) //大前提,最后一个结点走完时p=T,走完了一圈
{
while(p->LTag == Link) p=p->lchild; //一直向左
Print_1(p); //打印
while(p->RTag==Thread && p->rchild!=T) //RTag=Thread且不是最后一个结点时
{
p=p->rchild; Print_1(p);
}
p=p->rchild;
}
}
//递归遍历二叉树,以广义表形式输出
void Traverse_pre(BiTree *T,void (*Visit)(Elemtype e))
{
if(T)
{
Print_2(T->data);
if(T->lchild || T->rchild)
{
printf("(");
Traverse_pre(T->lchild,Print_2);
if(T->rchild) printf(",");
Traverse_pre(T->rchild,Print_2);
printf(")");
}
}
}
//判断数据是否相等,相等则返回该地址
BiTree *Panduan(BiTree *T)
{
if(T->data == ch) return T;
else return NULL;
}
//非递归遍历线索二叉树,查找期望键值的地址
BiTree *InOrder_Search(BiTree *T,BiTree *(*Visit)(BiTree *T))
{
//一直向左
BiTree *p=T->lchild;
while(p != T) //大前提,最后一个结点走完时p=T,走完了一圈
{
while(p->LTag == Link) p=p->lchild; //一直向左
if(Panduan(p)) return p; //判断,如果返回值存在就返回这个返回值
while(p->RTag==Thread && p->rchild!=T) //RTag=Thread且不是最后一个结点时
{
//判断,如果返回值存在就返回这个返回值
p=p->rchild; if(Panduan(p)) return p;
}
p=p->rchild;
}
}
//查找中序遍历指定结点的后继
void Search_post(BiTree *p) //p为目标结点的地址
{
BiTree *q,*temp;
if(p->RTag == Thread) temp = p->rchild; //如果右标志为线索,则线索指向的就是后继
if(p->RTag == Link) //右标志为结点,则向右一个,再左到底为后继
{
q=p->rchild;
while(q->LTag == Link) q = q->lchild;
temp = q;
}
printf("%c的后继是%c",p->data,temp->data);
}
//查找中序遍历指定结点的前继
void Search_pre(BiTree *p) //p为目标结点的地址
{
BiTree *q,*temp;
if(p->LTag == Thread) temp=p->lchild;//若左标志为线索,则线索就指向前继
if(p->LTag == Link) //若左标志为结点,则向左一个,再右到底为前继
{
q=p->lchild;
while(q->RTag == Link) q=q->rchild;
temp = q;
}
printf("%c的前继是%c",p->data,temp->data);
}
//主线索化函数调用的辅助递归线索化函数
BiTree *InThreading(BiTree *T,BiTree *pre)
{
if(T) //重中之重,递归条件T必须存在
{
pre=InThreading(T->lchild,pre);
//左孩子不存在Ltag就等于Thread,左孩子指向前驱线索pre
if(!T->lchild) {T->LTag = Thread; T->lchild = pre;}
//前驱线索pre不存在RTag就等于Thread,右孩子就等于该层的T
if(!pre->rchild) {pre->RTag = Thread; pre->rchild = T;}
//pre随着T做线性移动
pre = T;
pre=InThreading(T->rchild,pre);
}
return pre; //返回pre的值
}
//主线索化函数,主要开辟头结点、调用辅助递归线索化函数、处理头结点及最后一个结点
BiTree *InOrderThreading(BiTree *T)
{
//开辟头结点(LTag=Link,lchild指向树根RTag=Thread,rchild指向自身)
//定义一个前驱指针pre
BiTree *pre,*Thrt = (BiTree *)malloc(sizeof(BiTree));
if(!Thrt) printf("1失败");
//头结点lchild指向树,rchild回指
Thrt->LTag = Link; Thrt->RTag = Thread; Thrt->rchild = Thrt;
if(!T) Thrt->lchild = Thrt;
else
{
Thrt->lchild = T; pre = Thrt; //pre指向头结点
pre=InThreading(T,pre); //线索化
//线索化树的最后一个结点
pre->RTag = Thread; pre->rchild = Thrt;
Thrt->rchild = pre;
}
return Thrt; //返回头结点地址
}
//先序创建线索二叉树,初始化双亲地址,LTag、RTag
BiTree *Create(BiTree *T,BiTree *p) //p为给parent赋值的指针,初始为NULL
{
Elemtype i;
scanf("%c",&i);
if(i=='#') T=NULL;
else
{
T=(BiTree *)malloc(sizeof(BiTree));
if(!T) return ;
T->data=i;
T->parent=p;
p=T;
T->LTag=Link; //重要的两步,如果不初始化值后面就会出错
T->RTag=Link;
T->lchild = Create(T->lchild,p);
T->rchild = Create(T->rchild,p);
}
return T;
}
int main()
{
BiTree *T,*p=NULL,*q;
printf("先序递归创建二叉树:\n");
T=Create(T,p);
printf("以广义表的形式打印二叉树:\n");Traverse_pre(T,Print_2);
printf("\n");
T=InOrderThreading(T); //已加入头结点
printf("中序遍历的结果:从上到下\n");
InOrderTraverseThread(T,Print_1); //传递头结点
ch='A'; //全局变量
q=InOrder_Search(T,Panduan); //返回的是键值A的地址
printf("查找中序遍历A的后继\n####################\n");
Search_post(q);
printf("\n");
printf("查找中序遍历A的前继继\n####################\n");
Search_pre(q); //传输的是键值A的地址
return 0;
}
6、测试
这些代码都是前人的智慧所凝聚的精华,我们要做的就是继承,这就是所谓的站在巨人的肩膀上。
有的同学会认为学习这些算法没什么用处,千万别这么想!学习是面向过程的,在学习的过程中锻炼的是你自己的学习能力,而不在于学习的东西是否有用处。
可能我们从小到大一直在学习没什么用的知识,但还是过来了。在学习的过程中要注重方法,这就是你学习能力的体现,比如说把复杂的问题简单化,一步一步把问题解决,答案自然浮现在眼前。
这就是在学习的过程中锻炼的能力,使自己办事情的效率更高,在以后的学习工作中也就可以站得更高看得更远。