二叉树相关操作(基于递归,C语言实现)

二叉树相关操作(基于递归,C语言实现)

一、前序方式创建二叉树

​ 由于前序方式是从根节点开始,所以建立用来建立二叉树比较方便。并且后面的相关操作也基本上是基于前序遍历的方式。

​ 这里,我用’#'表示该结点位置为空,英文字母表示该节点的值。

​ 因此,接收后台输入时,当遇到’#'时,赋值为空,否则,为其分配内存空间,并为该结点赋值,接着继续递归创建其左孩子和右孩子。

//前序方式建立 
BTnode *CreateTree()
{
	BTnode *T;
	char ch;
	scanf("%c",&ch);//在后台直接输入一整串,例如: AB#D##CE##F## 
	if(ch=='#')
		T = NULL;
	else
	{
		T=(BTnode *)malloc(sizeof(BTnode));
		T->data=ch;//根节点赋值 
		T->lchild=CreateTree();//创建左孩子 
		T->rchild=CreateTree();//创建右孩子 
	}
	return T;//返回
}

二、3种递归方式遍历

2.1 前序遍历

​ 递归方式:根-左-右

//先序遍历二叉树--根左右 
void Preorder(BTnode *T)
{
	if(T!=NULL)
		printf("%c ",T->data);//根 
	if(T->lchild!=NULL)
		Preorder(T->lchild);//左 
	if(T->rchild!=NULL)
		Preorder(T->rchild);//右 
}

2.2 中序遍历

​ 递归方式:左-根-右

//中序遍历二叉树--左根右 
void Inorder(BTnode *T)
{
	if(T!=NULL)
	{
		if(T->lchild!=NULL)
			Inorder(T->lchild);//左 
		printf("%c ",T->data);//根 
		if(T->rchild!=NULL)
			Inorder(T->rchild);//右 
	}
}

2.3 后序遍历

​ 递归方式:左-右-根

//后序遍历二叉树--左右根 
void Postorder(BTnode *T)
{
	if(T!=NULL)
	{ 
		if(T->lchild!=NULL)
			Postorder(T->lchild);//左 
		if(T->rchild!=NULL)
			Postorder(T->rchild);//右 
		printf("%c ",T->data);//根 
	}
}

三、求叶子结点(度为0)个数

​ 我们知道度为0的结点即为叶子结点,因此当我们用前序方式递归遍历遇到左右孩子均为空时,叶子结点的数量加一。

//求叶子结点个数 
int CountLeaf(BTnode *T)
{
	static int count = 0;
	if(T!=NULL)//根结点 
	{
		if((T->lchild==NULL)&&(T->rchild==NULL))
			count++;//如果同时没有左孩子和右孩子那么就是叶子结点
		if(T->lchild!=NULL)
			CountLeaf(T->lchild);//左孩子 
		if(T->rchild!=NULL)
			CountLeaf(T->rchild);//右孩子 
	}
	return count; 
}

四、求度为1的结点个数

​ 度为1的结点,即必须满足:①左孩子空,右孩子非空;②左孩子非空,右孩子空。是一种排他性或。

​ 在C语言逻辑上表现为:((T->lchild==NULL)&&(T->rchild!=NULL))||((T->lchild!=NULL)&&(T->rchild==NULL))

​ 这种是错误的:((T->lchild==NULL)||(T->rchild==NULL))

//求度为 1 的结点个数 
int Count_1_Degree(BTnode *T)
{
	static int count = 0;
	if(T!=NULL)//根结点 
	{
		if(((T->lchild==NULL)&&(T->rchild!=NULL))||((T->lchild!=NULL)&&(T->rchild==NULL)))
			count++;//度为 1 的结点只有一个左孩子或只有一个右孩子 
		if(T->lchild!=NULL)
			Count_1_Degree(T->lchild);//左孩子 
		if(T->rchild!=NULL)
			Count_1_Degree(T->rchild);//右孩子 
	}
	return count; 
}

五、求度为2的结点个数

​ 度为2就不用多说了,也就是找左右孩子都不为空的结点。

//求度为 2 的结点个数 
int Count_2_Degree(BTnode *T)
{
	static int count = 0;
	if(T!=NULL)//根结点 
	{
		if((T->lchild!=NULL)&&(T->rchild!=NULL))
			count++;//度为 2 的结点既有左孩子又有右孩子 
		if(T->lchild!=NULL)
			Count_2_Degree(T->lchild);//左孩子 
		if(T->rchild!=NULL)
			Count_2_Degree(T->rchild);//右孩子 
	}
	return count; 
}

六、求某结点的左右孩子

​ 求左右孩子也比较简单,首先基于前序递归遍历,设ch为我们要找的目标结点的值,当T->data==ch为真时,也就是找到了目标结点,再看它左右孩子是否为空,如果为空则直接说没有左孩子或没有右孩子,如果不空就各自输出其左右孩子的值。

//求某结点的左右孩子 
void Get_children(BTnode *T,char ch)
{
	if(T!=NULL)//根结点 
	{
		if(T->data==ch)//找到该结点了 
		{
			if(T->lchild!=NULL)
				printf("\n%c的左孩子结点为:%c\n",ch,T->lchild->data);
			else
				printf("\n%c没有左孩子!\n",ch);
			if(T->rchild!=NULL)
				printf("\n%c的右孩子结点为:%c\n",ch,T->rchild->data);
			else
				printf("\n%c没有右孩子!\n",ch);
			return ;//返回空 
		}
			
		//没找到目标结点 
		if(T->lchild!=NULL)
			Get_children(T->lchild,ch);//左孩子 
		if(T->rchild!=NULL)
			Get_children(T->rchild,ch);//右孩子 
	} 
	return ;//返回空 
}

七、求某结点的父结点

​ 求父结点时,一开始遇到了一些bug,不过主要是逻辑先后的问题。只有当左右孩子不为空时,才能去判断其数据域是否等于目标结点,否则可能无法正常退出。

//求某结点的父结点 
void Get_Father(BTnode *T,char ch)
{
	if(T==NULL)
		return;
	if(T!=NULL)//根结点 
	{
		if(T->lchild!=NULL){//这一行判断至关重要,少了可能会无法正常退出
			if(T->lchild->data==ch)//找到该结点了 
			{
				printf("\n%c的父结点为:%c\n",ch,T->data);
				return ;//返回空,可能会影响后续的操作进行 
			}
		}
		
		if(T->rchild!=NULL){//这一行判断至关重要,少了可能会无法正常退出
			if(T->rchild->data==ch)//找到该结点了 
			{
				printf("\n%c的父结点为:%c\n",ch,T->data);
				return ;//返回空,可能会影响后续的操作进行 
			}
		}
	}
	Get_Father(T->lchild,ch);//左孩子 
	Get_Father(T->rchild,ch);//右孩子
}

八、求一共有多少个结点

​ 找一共有几个结点就更简单了,可以调用前面的求度为0、度为1和度为2的函数,把它们的结果相加就可以了,因为在二叉树中,结点的度只有以上3种情况。当然我这里还是手动再遍历了一下,前序遍历T,如果T不为空,那么直接++。

//求结点个数 
int Get_Node_Nums(BTnode *T)
{
	static int count = 0;
	if(T!=NULL)
		count++;
	if(T->lchild!=NULL)
		Get_Node_Nums(T->lchild);//左 
	if(T->rchild!=NULL)
		Get_Node_Nums(T->rchild);//右 
	return count;
}

九、求二叉树的高度

​ 求二叉树的高度:递归求出左子树和右子树的高度,比较左右子树的高度,取最大的高度,再加上 1(根节点所在的层)即为树高
​ 即 :height = max(lheight,rheight) + 1
​ 求高度时参考了这篇文章: https://www.cnblogs.com/xielei/p/10603084.html

int Get_Tree_Height(BTnode *T)
{
	if(T==NULL)//如果树为空,直接返回0 
		return 0;
	else
	{
		int lheight = Get_Tree_Height(T->lchild);//求左子树的高 
		int rheight = Get_Tree_Height(T->rchild);//求右子树的高
		return  (lheight>rheight)?(lheight+1):(rheight+1);
	}
}

十、销毁树

​ 销毁,前序遍历,然后一个个free掉。

//销毁树 
bool Destroy_Tree(BTnode *T)
{
	if(T==NULL)//根结点 
		return true;
	if(T!=NULL){
		Destroy_Tree(T->lchild);
		Destroy_Tree(T->rchild);
	}
	free(T);
}

全部代码

#include "stdio.h"
#include"stdlib.h"
#include<stdbool.h> //根据C99标准,C语言使用bool类型需要添加这个头文件

typedef struct node
{  
	char data;//数据域 
	struct node *lchild,*rchild;//分别指向左右孩子的指针 
}BTnode;

//前序方式建立 
BTnode *CreateTree()
{
	BTnode *T;
	char ch;
	scanf("%c",&ch);//在后台直接输入一整串,例如: AB#D##CE##F## 
	if(ch=='#')
		T = NULL;
	else
	{
		T=(BTnode *)malloc(sizeof(BTnode));
		T->data=ch;//根节点赋值 
		T->lchild=CreateTree();//创建左孩子 
		T->rchild=CreateTree();//创建右孩子 
	}
	return T;
}

//先序遍历二叉树--根左右 
void Preorder(BTnode *T)
{
	if(T!=NULL)
		printf("%c ",T->data);//根 
	if(T->lchild!=NULL)
		Preorder(T->lchild);//左 
	if(T->rchild!=NULL)
		Preorder(T->rchild);//右 
}

//中序遍历二叉树--左根右 
void Inorder(BTnode *T)
{
	if(T!=NULL)
	{
		if(T->lchild!=NULL)
			Inorder(T->lchild);//左 
		printf("%c ",T->data);//根 
		if(T->rchild!=NULL)
			Inorder(T->rchild);//右 
	}
}

//后序遍历二叉树--左右根 
void Postorder(BTnode *T)
{
	if(T!=NULL)
	{ 
		if(T->lchild!=NULL)
			Postorder(T->lchild);//左 
		if(T->rchild!=NULL)
			Postorder(T->rchild);//右 
		printf("%c ",T->data);//根 
	}
}

//求叶子结点个数 --度为 0 
int CountLeaf(BTnode *T)
{
	static int count = 0;
	if(T!=NULL)//根结点 
	{
		if((T->lchild==NULL)&&(T->rchild==NULL))
			count++;//如果同时没有左孩子和右孩子那么就是叶子结点
		if(T->lchild!=NULL)
			CountLeaf(T->lchild);//左孩子 
		if(T->rchild!=NULL)
			CountLeaf(T->rchild);//右孩子 
	}
	return count; 
}

//求度为 1 的结点个数 
int Count_1_Degree(BTnode *T)
{
	static int count = 0;
	if(T!=NULL)//根结点 
	{
		if(((T->lchild==NULL)&&(T->rchild!=NULL))||((T->lchild!=NULL)&&(T->rchild==NULL)))
			count++;//度为 1 的结点只有一个左孩子或只有一个右孩子 
		if(T->lchild!=NULL)
			Count_1_Degree(T->lchild);//左孩子 
		if(T->rchild!=NULL)
			Count_1_Degree(T->rchild);//右孩子 
	}
	return count; 
}

//求度为 2 的结点个数 
int Count_2_Degree(BTnode *T)
{
	static int count = 0;
	if(T!=NULL)//根结点 
	{
		if((T->lchild!=NULL)&&(T->rchild!=NULL))
			count++;//度为 2 的结点既有左孩子又有右孩子 
		if(T->lchild!=NULL)
			Count_2_Degree(T->lchild);//左孩子 
		if(T->rchild!=NULL)
			Count_2_Degree(T->rchild);//右孩子 
	}
	return count; 
}

//求某结点的左右孩子 
void Get_children(BTnode *T,char ch)
{
	if(T!=NULL)//根结点 
	{
		if(T->data==ch)//找到该结点了 
		{
			if(T->lchild!=NULL)
				printf("\n%c的左孩子结点为:%c\n",ch,T->lchild->data);
			else
				printf("\n%c没有左孩子!\n",ch);
			if(T->rchild!=NULL)
				printf("\n%c的右孩子结点为:%c\n",ch,T->rchild->data);
			else
				printf("\n%c没有右孩子!\n",ch);
			return ;//返回空 
		}
			
		//没找到目标结点 
		if(T->lchild!=NULL)
			Get_children(T->lchild,ch);//左孩子 
		if(T->rchild!=NULL)
			Get_children(T->rchild,ch);//右孩子 
	} 
	return ;//返回空 
}

//求某结点的父结点 
void Get_Father(BTnode *T,char ch)
{
	if(T==NULL)
		return;
	if(T!=NULL)//根结点 
	{
		if(T->lchild!=NULL){//这一行判断至关重要,少了可能会无法正常退出
			if(T->lchild->data==ch)//找到该结点了 
			{
				printf("\n%c的父结点为:%c\n",ch,T->data);
				return ;//返回空,可能会影响后续的操作进行 
			}
		}
		
		if(T->rchild!=NULL){//这一行判断至关重要,少了可能会无法正常退出
			if(T->rchild->data==ch)//找到该结点了 
			{
				printf("\n%c的父结点为:%c\n",ch,T->data);
				return ;//返回空,可能会影响后续的操作进行 
			}
		}
	}
	Get_Father(T->lchild,ch);//左孩子 
	Get_Father(T->rchild,ch);//右孩子
	
}

//销毁树 
bool Destroy_Tree(BTnode *T)
{
	if(T==NULL)//根结点 
		return true;
	if(T!=NULL){
		Destroy_Tree(T->lchild);
		Destroy_Tree(T->rchild);
	}
	free(T);
	return true;
}

//求结点个数 
int Get_Node_Nums(BTnode *T)
{
	static int count = 0;
	if(T!=NULL)
	{
		count++;
		if(T->lchild!=NULL)
			Get_Node_Nums(T->lchild);//左 
		if(T->rchild!=NULL)
			Get_Node_Nums(T->rchild);//右 
	}
	return count;
}

//求二叉树的高度:递归求出左子树和右子树的高度,比较左右子树的高度,取最大的高度,再加上 1(根节点所在的层)即为树高
// 即 height = max(lheight,rheight) + 1 
int Get_Tree_Height(BTnode *T)
{
	if(T==NULL)//如果树为空,直接返回0 
		return 0;
	else
	{
		int lheight = Get_Tree_Height(T->lchild);//求左子树的高 
		int rheight = Get_Tree_Height(T->rchild);//求右子树的高
		return  (lheight>rheight)?(lheight+1):(rheight+1);
	}
}

int main()
{
	BTnode *T;
	int count_leaves = 0,degree_1 = 0, degree_2 = 0;
	printf("请根据前序方式输入序列(对于一个结点,如果其无左右孩子,则输入两个#):\n") ;
	T = CreateTree();//测试示例->  ABD#E###CF##G##
	
	printf("\n前序遍历结果:\n");
	Preorder(T);
	printf("\n");
	printf("中序遍历结果:\n");
	Inorder(T);
	printf("\n");
	printf("后序遍历结果:\n");
	Postorder(T);
	
	
	count_leaves = CountLeaf(T);
	printf("\n\n叶子结点的个数为:%d\n",count_leaves);
	degree_1 = Count_1_Degree(T);
	printf("\n度为1的结点的个数为:%d\n",degree_1);
	degree_2 = Count_2_Degree(T);
	printf("\n度为2的结点的个数为:%d\n",degree_2);
	
	//求一共有几个结点 
	int nodes = Get_Node_Nums(T);
	printf("\n该二叉树一共有%d个结点!\n",nodes);
	
	//求树高
	int height = Get_Tree_Height(T);
	printf("\n树的高度为:%d\n",height);
	
	//求孩子结点 
	Get_children(T,'A'); 
	Get_children(T,'B');
	Get_children(T,'C');
	Get_children(T,'D');
	Get_children(T,'E');

	//求父结点 
	Get_Father(T,'B');//测试示例->  ABD#E###CF##G##  
	Get_Father(T,'C');//
	Get_Father(T,'D');//
	Get_Father(T,'E');//
	Get_Father(T,'F');//
	Get_Father(T,'G');//
	
	//最后销毁树 
	if(Destroy_Tree(T))
		printf("\n销毁成功!\n");//销毁树
	else
		printf("销毁失败!"); 
	
	return 0;
}

测试

示例:

image-20211101215653245

输入:ABD#E###CF##G##

输出:

image-20211101215521554

image-20211101215543857

注:我这里为了实现功能,有些部分可能没有考虑将代码进一步精简。

欢迎各位批评指正。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值