C语言数据结构二叉树的建立并中序线索化

一提到线索化二叉树,可能有些小伙伴的头都要炸了!如果你遇到了问题,可以参照一下这个版本,希望对你有帮助。

内容:

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、测试



这些代码都是前人的智慧所凝聚的精华,我们要做的就是继承,这就是所谓的站在巨人的肩膀上。

有的同学会认为学习这些算法没什么用处,千万别这么想!学习是面向过程的,在学习的过程中锻炼的是你自己的学习能力,而不在于学习的东西是否有用处。

可能我们从小到大一直在学习没什么用的知识,但还是过来了。在学习的过程中要注重方法,这就是你学习能力的体现,比如说把复杂的问题简单化,一步一步把问题解决,答案自然浮现在眼前。

这就是在学习的过程中锻炼的能力,使自己办事情的效率更高,在以后的学习工作中也就可以站得更高看得更远。



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章