【二叉树】的实现

news2025/1/21 0:47:28

📙作者简介: 清水加冰,目前大二在读,正在学习C/C++、Python、操作系统、数据库等。

📘相关专栏:C语言初阶、C语言进阶、C语言刷题训练营、数据结构刷题训练营、有感兴趣的可以看一看。

欢迎点赞 👍 收藏 ⭐留言 📝 如有错误还望各路大佬指正!

✨每一次努力都是一种收获,每一次坚持都是一种成长✨       

在这里插入图片描述

目录

 前言

构建二叉树

 销毁二叉树

 二叉树的递归遍历

 二叉树节点个数

 二叉树叶子节点个数

 二叉树第k层节点个数

查找

 二叉树的高度

二叉树的层序遍历

判断是否是完全二叉树

 总结


 前言

        前边的内容陆续的对二叉树的内容进行了介绍,但也并没有系统的将二叉树各部分的操作进行实现,本期将会对二叉树的一些基本操作进行实现。


构建二叉树

        二叉树的基本操作中不包含插入和删除操作(没有意义),在后续更为复杂的树形结构中才有使用价值,这里就不再进行实现。

        之前都是一直手动构建二叉树,一个一个的创建节点连接,在创建二叉树时我们还可以通过递归创建二叉树。牛客上边也有相关题目:

二叉树的构建及遍历

         我们也以这道题目为例,来进行构建二叉树。题目给的要求是根据输入的字符串来构建二叉树,#就代表为NULL。最后我们返回这棵树的根即可。

 在此之前我们先定义一下二叉树:

typedef char Datatype;
typedef struct BinaryTree
{
	Datatype data;
	struct BinaryTree* left;
	struct BinaryTree* right;
}BTNode;

        根据输入的字符串来进行二叉树的构建。传进来一个一个数组,链式二叉树的构建最简洁的是使用递归,但是我们也需要有一个变量控制一下遍历的字符位置,在函数里创建一个变量不好进行传参,所以我们最好在调用函数里创建一个变量,然后取地址传参。

具体代码如下:

BTNode* CreatBTNode(char* str,int* pi)
{
    if(str[*pi]=='#')
    {
        (*pi)++;
        return NULL;
    }
    //创建节点
    BTNode* root =(BTNode*)malloc(sizeof(BTNode));
    //节点赋值
    root->data=str[*pi];
    (*pi)++;
    //连接
    root->left=CreatBTNode(str,pi);
    root->right=CreatBTNode(str,pi);

    return root;
}

 销毁二叉树

        销毁二叉树需要注意二叉树根的置空,这里我们可以使用一级指针也可以使用二级指针。

 使用一级指针置空root时需要调用销毁函数后手动置空。销毁二叉树同样也需要利用递归遍历二叉树,然后逐一销毁。

 二级指针:

void TreeDestroy(BTNode** root)
{
	if (*root == NULL)
	{
		return;
	}

	TreeDestroy(&(*root)->left);
	TreeDestroy(&(*root)->right);
	free(*root);
	root = NULL;
}

一级指针:

void TreeDestroy(BTNode* root)
{
	if (root == NULL)
	{
		return;
	}

	TreeDestroy(root->left);
	TreeDestroy(root->right);
	free(root);
	
}

 二叉树的递归遍历

 遍历的内容较为简单,前边文章也介绍过,这里作为整合,详细可见:数据结构入门指南:二叉树

代码如下:

//前序遍历
void PrevOrder(BTNode* root)
{
	if (root == NULL)
	{
		printf("NULL ");
		return;
	}
	printf("%c ", root->data);
	PrevOrder(root->left);
	PrevOrder(root->right);
}
//中序遍历
void InOrder(BTNode* root)
{
	if (root == NULL)
	{
		printf("NULL ");
		return;
	}
	InOrder(root->left);
	printf("%c ", root->data);
	InOrder(root->right);
}
//后续遍历
void PostOrder(BTNode* root)
{
	if (root == NULL)
	{
		printf("NULL ");
		return;
	}
	PostOrder(root->left);
	PostOrder(root->right);
	printf("%c ", root->data);

}

 二叉树节点个数

这个就很简单了,我们前边也实现过,就不再详细介绍。详细可见文章:二叉树】——链式结构(快速掌握递归与刷题技巧)

int TreeSize(BTNode* root)
{
	if (root == NULL)
		return 0;
	return TreeSize(root->left) + TreeSize(root->right) + 1;
}

 二叉树叶子节点个数

这部分是对节点个数的一个进阶,代码如下:

int BinaryTreeLeafSize(BTNode* root)
{
	if (root==NULL)
	{
		return 0;
	}
	if (root->left == NULL && root->right == NULL)
	{
		return 1;
	}
	return BinaryTreeLeafSize(root->left) + BinaryTreeLeafSize(root->right);
}

 二叉树第k层节点个数

之前文章中讲过,这里就整合一下,不进行详细介绍,代码如下:

int BinaryTreeLevelKSize(BTNode* root, int k)
{
	assert(k > 0);
	if (root == NULL)
	{
		return 0;
	}
	if (k == 1)
	{
		return 1;
	}
	return BinaryTreeLevelKSize(root->left, k - 1) + BinaryTreeLevelKSize(root->right, k - 1);
}

查找

         二叉树查找部分不难,但存在坑,我们依然是使用递归遍历来查找,如果root为NULL就返回NULL,找到符合的data就返回当前节点。在查找时其实就是对二叉树的一种遍历,我们先判断当前节点是否符合,再判断左子树和右子树。它其实考察的就是二叉树的前序遍历。

BTNode* TreeFind(BTNode* root, char x)
{
	if (root == NULL)
	{
		return NULL;
	}
	if (root->data == x)
	{
		return root;
	}
	else
	{
		BTNode* ret1=TreeFind(root->left, x);
		BTNode* ret2=TreeFind(root->right, x);
		return ret1 != NULL ? ret1 : ret2;
	}
}

         最后返回时返回不为NULL的那个,所以我们这里是先假设:ret1!=NULL,不为NULL就返回ret1,否则(ret1为NULL),就返回ret2。

         或许有人会有疑问为什么不直接使用运算符还要创建两个变量来接收返回值?

        这里我们要明白,如果直接将递归带入运算符,那么它在判断条件时就会执行一次,在最终结果时又会执行一遍,这样虽然不会造成空间上多大的浪费,但在时间上就会大大降低,每使用一次就会递归一次,这都是时间的消耗。

 二叉树的高度

        要计算二叉树的高度,也就是找出左子树和右子树中层数最高的然后加一。当遇到节点为NULL就返回0。

 代码如下:

int TreeHeight(BTNode* root)
{
	if (root == NULL)
		return 0;

	int leftHeight = TreeHeight(root->left);
	int rightHeight = TreeHeight(root->right);

	return leftHeight > rightHeight ? leftHeight + 1 : rightHeight + 1;
}

二叉树的层序遍历

         二叉树的层序遍历与之前的递归遍历二叉树又有所不同,层序遍历需要使用到队列的数据结构。层序遍历是属于广度优先的遍历,而前序遍历属于深度优先的遍历

 前边的递归遍历是通过根(root)找到左子树,右子树,然后一直不断的向下遍历。而层序遍历不同,它是通过一层带一层的方法来遍历的,过程如下图:

 要实现层序遍历首先我们需要有一个队列的数据结构(可以拷贝之前的C语言代码),然后通过不断的入队出队操作,来实现一层带一层的遍历。

void LayerTraver(BTNode* root)
{
	Que q;//创建队列
	QueueInit(&q);//初始化队列
	if(root)//判断根是否为NULL,不为NULL就入队
		QueuePush(&q, root);
	while (!IsEmpty(&q))
	{
		BTNode* front= QueueFront(&q);//取队头数据

        //通过队头数据找到左右子节点

		if (front->left)
		{
			QueuePush(&q, front->left);
		}
		if (front->right)
		{
			QueuePush(&q, front->right);
		}
        //输出队头节点的数据,也可以存放到数组中
		printf("%d ", front->data);
        //队头节点的左右子节点入队之后,将节点1出队
		QueuePop(&q);
	}
	DestoryQueue(&q);
}

判断是否是完全二叉树

         通过层序遍历的原理,我们还可以通过层序遍历来解决判断完全二叉树的问题。在入队时如果第一次遇到NULL就跳出循环,然后再入队,如果入队的有非空节点,就返回false,没有则返回true。代码如下:

int TreeComplete(BTNode* root)
{
	Que q;
	QueueInit(&q);

	if (root)
		QueuePush(&q, root);

	while (!IsEmpty(&q))
	{
		BTNode* front = QueueFront(&q);
		if (front == NULL)//第一次遇到NULL,跳出循环
			break;

		QueuePush(&q, front->left);
		QueuePush(&q, front->right);
		QueuePop(&q);
	}
//再次进行层序遍历,入队
	while (!IsEmpty(&q))
	{
		BTNode* front = QueueFront(&q);
		QueuePop(&q);
        //如果取到的队头节点有非空节点,则不是完全二叉树
		if (front != NULL)
		{
			DestoryQueue(&q);
			return false;
		}
	}
//没有非空节点最后返回true
	DestoryQueue(&q);
	return true;
}

 总结

        以上便是本期全部内容,二叉树篇到此结束,本篇文章是对二叉树一些基本操作的整合,以及层序遍历的介绍与讲解,希望可以帮到你,最后,感谢阅读!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1064095.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

竞赛选题 深度学习 python opencv 动物识别与检测

文章目录 0 前言1 深度学习实现动物识别与检测2 卷积神经网络2.1卷积层2.2 池化层2.3 激活函数2.4 全连接层2.5 使用tensorflow中keras模块实现卷积神经网络 3 YOLOV53.1 网络架构图3.2 输入端3.3 基准网络3.4 Neck网络3.5 Head输出层 4 数据集准备4.1 数据标注简介4.2 数据保存…

中断:ZYNQ

整个中断系统架构中,只包含以下三种中断:  软件生成中断(Software Generated Interrupts,SGI)  私有外设中断(Private Peripheral Interrupts,PPI)  共享外设中断(Shared Peripheral Interrupts,SPI) 每种中断…

华为云云耀云服务器L实例评测|云耀云服务器L实例部署DjangoBlog个人博客系统

华为云云耀云服务器L实例评测|云耀云服务器L实例部署DjangoBlog个人博客系统 一、云耀云服务器L实例介绍1.1 云耀云服务器L实例简介1.2 云耀云服务器L实例特点 二、DjangoBlog介绍2.1 DjangoBlog介绍2.2 DjangoBlog特点 三、本次实践介绍3.1 本次实践简介3.2 本次环…

java socket实现代理Android App

实现逻辑就是转发请求和响应。 核心代码 // 启动代理服务器private void startProxyServer() {new Thread(new ProxyServer()).start();}// 代理服务器static class ProxyServer implements Runnable {Overridepublic void run() {try {// 监听指定的端口int port 8098; //一…

AutoGen - 多个Agent开发LLM应用的框架

文章目录 关于安装使用关于 Enable Next-Gen Large Language Model Applications 用多个Agent开发LLM应用的框架,这些agent可相互交流以解决任务。 官网:https://microsoft.github.io/autogen/github : http://github.com/microsoft/autogendiscord : https://discord.com/i…

LVGL_基础控件滚轮roller

LVGL_基础控件滚轮roller 1、创建滚轮roller控件 /* 创建一个 lv_roller 部件(对象) */ lv_obj_t * roller lv_roller_create(lv_scr_act()); // 创建一个 lv_roller 部件(对象),他的父对象是活动屏幕对象// 将部件(对象)添加到组,如果设置了默认组&#xff0c…

buuctf-crypto 1

rsarsa 题目描述 Math is cool! Use the RSA algorithm to decode the secret message, c, p, q, and e are parameters for the RSA algorithm.p 96484230290105156765905517400104265349457376392357398006439893520398525072984913995610350091634270503701075707336333…

Java循环队列

目录 一、循环队列 二、设计循环队列 一、循环队列 队列:只能在一端进行插入数据操作,另一端进行删除数据操作的特殊线性表,是一种先进先出的存储结构 插入操作的一端为队尾,删除操作的一端为队头 在线性队列中,一…

leetcode 62. 不同路径、63.不同路径||

62. 不同路径 一个机器人位于一个 m x n 网格的左上角 (起始点在下图中标记为 “Start” )。 机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角(在下图中标记为 “Finish” )。 问总共有多少条不同的路径&…

课题学习(三)----倾角和方位角的动态测量方法(基于陀螺仪的测量系统)

一、内容介绍 该测量系统基于三轴加速度和三轴陀螺仪,安装在钻柱内部,随钻柱一起旋转,形成捷联惯性导航系统,安装如下图所示:   假设三轴加速度和陀螺仪的输出为: f b [ f x f y f z ] T f^b\begin{bmatrix}f_{x} …

C++ 学习系列 -- std::list

一 std::list 介绍 list 是 c 中的序列式容器,其实现是双向链表,每个元素都有两个指针,分别指向前一个节点与后一个节点 链表与数组都是计算机常用的内存数据结构,与数组连续内存空间不一样的地方在于,链表的空间是不…

Springboot实现登录功能(token、redis、登录拦截器、全局异常处理)

登录流程: 1、前端调用登录接口,往接口里传入账号,密码 2、根据账号判断是否有这个用户,如果有则继续判断密码是否正确 3、验证成功后,则是根据账号,登录时间生成token(用JWT) 4、将…

64位Office API声明语句第111讲

跟我学VBA,我这里专注VBA, 授人以渔。我98年开始,从源码接触VBA已经20余年了,随着年龄的增长,越来越觉得有必要把这项技能传递给需要这项技术的职场人员。希望职场和数据打交道的朋友,都来学习VBA,利用VBA,起码可以提高…

企业使用SSL证书对于SEO有多重要

在当今竞争激烈的在线市场中,搜索引擎优化(SEO)是企业获得更高排名和增加网站流量的关键。在SEO策略中,企业使用SSL证书已经成为多重不可忽视的重要因素。让我们一起探究企业使用SSL证书对于SEO的重要性。 首先,搜索引…

socket.error: [Errno 10049]错误

今天在pycharm运行rl_server_no_training.py欲启动服务器时&#xff0c;却出现如下错误 Traceback (most recent call last):File "xxx/rl_server_no_training.py", line 333, in <module>main()File "xxx/rl_server_no_training.py", line 326, in…

【C++】运算符重载 ⑧ ( 左移运算符重载 | 友元函数 / 成员函数 实现运算符重载 | 类对象 使用 左移运算符 )

文章目录 一、左移运算符重载1、友元函数 / 成员函数 实现运算符重载2、类对象 使用 左移运算符3、左移运算符 << 重载 二、完整代码示例 一、左移运算符重载 1、友元函数 / 成员函数 实现运算符重载 运算符重载 的正规写法一般都是 使用 成员函数 的形式 实现的 ; 加法…

DLRover - 小记

文章目录 关于 DLRover 关于 DLRover github : https://github.com/intelligent-machine-learning/dlrover DLOver使大型人工智能模型的分布式训练变得简单、稳定、快速和绿色。 它可以在分布式集群上自动训练深度学习模型。 它帮助模型开发人员专注于模型结构&#xff0c;而…

手把手教你从零开始腾讯云服务器部署(连接建站教程)

使用腾讯云服务器搭建网站全流程&#xff0c;包括轻量应用服务器和云服务器CVM建站教程&#xff0c;轻量可以使用应用镜像一键建站&#xff0c;云服务器CVM可以通过安装宝塔面板的方式来搭建网站&#xff0c;腾讯云服务器网txyfwq.com分享使用腾讯云服务器建站教程&#xff0c;…

双周回顾#001 - 火烧云

工作以来&#xff0c;许久未曾见过如此壮观的火烧云景观了。 文章 1、穿透的 DIV &#xff08;pointer-events&#xff09;1 pointer-events 是一个蛮有趣的 CSS3 属性&#xff0c;虽然主要是针对 SVG &#xff0c;但其中几个属性应用在 div 上也是颇有意思。 顾名思义&…

学会自我投资:美团四大名著之<高效能人士的七个习惯>

你有没有这些问题&#xff1f;如果有&#xff0c;那么本文也许对你有用。 受害者心理&#xff0c;如果有人对你说粗话&#xff0c;就怼回去。消极被动。 没有计划。没有目标。不会担心自己的行为带来的后果。随波逐流。及时行乐&#xff0c;做个玩世不恭者。 拖延。总是先做紧…