二叉树相关操作(基于递归,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;
}
测试
示例:
输入:ABD#E###CF##G##
输出:
注:我这里为了实现功能,有些部分可能没有考虑将代码进一步精简。
欢迎各位批评指正。