【数据结构】详解二叉树及其操作

news2024/9/20 6:23:11

无论你觉得自己多么的了不起,也永远有人比你更强。💓💓💓

目录

  ✨说在前面

🍋知识点一:二叉树的遍历

 • 🌰1.创建一棵二叉树

• 🌰2.二叉树的遍历

•🔥前序遍历

•🔥中序遍历

•🔥后序遍历

•🔥层序遍历

•🔥给出两种遍历如何求二叉树?

🍋知识点二:二叉树基本方法

 • 🌰1.二叉树节点个数

• 🌰2.二叉树叶子节点个数

• 🌰3.二叉树第k层节点个数

• 🌰4.二叉树的高度

• 🌰5.查找值为x的节点

• 🌰6.判断是否为完全二叉树

• 🌰7.二叉树的销毁

• ✨SumUp结语


  ✨说在前面

亲爱的读者们大家好!💖💖💖,我们又见面了,经过上一篇章中“堆”的学习,大家已经了解了树的基本结构。但是,堆只是树中特殊的一种数据结构,并且是基于树的一种数据结构。

今天我们将要学习它更加抽象的一面,即二叉树,那什么是二叉树,他们又是用什么来实现,又有什么作用呢?我们今天就详细剖析二叉树这一的数据结构吧~

在上一篇文章中,为了引入堆,也很清晰地介绍了树和二叉树的概念和性质,忘记的小伙伴可以去看看。

  👇👇👇
💘💘💘知识连线时刻(直接点击即可)

  🎉🎉🎉复习回顾🎉🎉🎉

         【数据结构】详解二叉树之堆

  博主主页传送门:愿天垂怜的博客

 

🍋知识点一:二叉树的遍历

 • 🌰1.创建一棵二叉树

在学习二叉树的基本操作之前,我们首先需要创建一棵二叉树,然后才能学习其相关的基本操作。但是由于现在我们知识有限,先手动创建一个二叉树一遍快速进入二叉树操作的学习,等以后我们再回过头来学习它真正的创建方式。

比如我们要创建如下的一棵二叉树:

代码如下:

#include <stdio.h>
#include <stdlib.h>
#include <assert.h>

//创建二叉树的数据结构
typedef int BTDatatype;

typedef struct BinaryTreeNode
{
	BTDatatype data;
	struct BinaryTreeNode* left;
	struct BinaryTreeNode* right;
}BTNode;

//创建二叉树的节点
static BTNode* BuyNode(BTDatatype x)
{
	BTNode* node = (BTNode*)malloc(sizeof(BTNode));
	if (node == NULL)
	{
		perror("malloc operation failed");
		exit(1);
	}
	node->data = x;
	node->left = node->right = NULL;
	return node;
}

//手动创建二叉树
BTNode* CreateBinaryTree()
{
	BTNode* node1 = BuyNode(1);
	BTNode* node2 = BuyNode(2);
	BTNode* node3 = BuyNode(3);
	BTNode* node4 = BuyNode(4);
	BTNode* node5 = BuyNode(5);
	BTNode* node6 = BuyNode(6);
	node1->left = node2;
	node1->right = node4;
	node2->left = node3;
	node4->left = node5;
	node4->right = node6;
	
	return node1;
}
注意:上面的代码不是创建二叉树的真正的方式,真正的二叉树是递归定义的,因此后序基本操作

    

• 🌰2.二叉树的遍历

学习二叉树结构,最简单的方式就是遍历。所谓二叉树遍历1(Traversal)是按照某种特定的规则,依次堆二叉树中的节点进行相应的操作,使得每个节点只操作依次。访问节点所做的操作依赖与具体的应用问题。

遍历是二叉树上最重要的运算之一,也是二叉树上进行其他运算的基础。

按照规则,二叉树的遍历有:前序/中序/后序的递归结构遍历;

🔥前序遍历(Prevorder Traversal)——访问根节点的操作发生在遍历其左右子树之前。

🔥中序遍历(Inorder Traversal)——访问根节点的操作发生在遍历其左右子树之中(间)。

🔥后序遍历(Postorder Traversal)——访问根节点的操作发生在遍历其左右子树之后。

由于被访问的节点必然是某子树的根,所以N(Node)、L(Left subtree)、R(Right subtree)又可以解释为根、根的左子树和根的右子树。NLR、LNR和LRN分别又称为先跟遍历、中根遍历和后根遍历。 

•🔥前序遍历

对于以下这一棵二叉树:

我们如果按照访问根节点的操作发生在遍历其左右子树之前,那么所能得到这样的遍历顺序:1,2,3,4,5,6,这样的遍历称之为前序遍历。对于这样一棵树,我们先访问左子树,再访问根,最后访问右子树,而左子树和右子树中又是按照根——左子树——右子树的顺序遍历,以此往复,直到遍历完成。

代码如下:

void PrevOrder(BTNode* root)
{
	if (root == NULL)
	{
		printf("N ");
		return;
	}
	printf("%d ", root->data);
	PrevOrder(root->left);
	PrevOrder(root->right);
}

上面代码完成了二叉树以前序遍历的方式打印每个节点中的值。.

•🔥中序遍历

对于以下这一棵二叉树:

我们如果按照访问根节点的操作发生在遍历其左右子树之中(间),那么所能得到这样的遍历顺序:3,2,1,5,4,6,这样的遍历称之为中序遍历。对于这样一棵树,我们先访问根,再访问左子树,最后访问右子树,而左子树和右子树中又是按照左子树——根——右子树的顺序遍历,以此往复,直到遍历完成。 

代码如下:

void InOrder(BTNode* root)
{
	if (root == NULL)
	{
		printf("N ");
		return;
	}
	InOrder(root->left);
	printf("%d ", root->data);
	InOrder(root->right);
}

 

•🔥后序遍历

 对于以下这一棵二叉树:

我们如果按照访问根节点的操作发生在遍历其左右子树之后,那么所能得到这样的遍历顺序:3,2,5,6,4,1,这样的遍历称之为后序遍历。对于这样一棵树,我们先访问左子树,再访问右子树,最后访问根,而左子树和右子树中又是按照左子树——右子树——根的顺序遍历,以此往复,直到遍历完成。 

代码如下:

void PostOrder(BTNode* root)
{
	if (root == NULL)
	{
		printf("N ");
		return;
	}
	PostOrder(root->left);
	PostOrder(root->right);
    printf("%d ", root->data);
}

 

•🔥层序遍历

除了先序遍历,中序遍历、后序遍历外,还可以对二叉树进行层序遍历,如下面这棵二叉树:

设二叉树的根节点 所在的层数为1,层序遍历就是从所在二叉树的根节点出发,首先访问第一层的树根节点,然后从左到右访问第二层上的节点,接着是第三层的节点,以此类推,自上而下,自左至右逐层访问树的节点的过程就是层序遍历。

实现二叉树的层序遍历,我们需要借助另一数据结构——队列。我们需要将根节点入队列,再每删除一个节点时,进行需要的操作,并将它的左右子节点入队列即可。

代码如下:

void TreeLevelOrder(BTNode* root)
{
	Queue q;
	QueueInit(&q);
	if (root)
		QueuePush(&q, root); 
	while (!QueueEmpty(&q))
	{
		BTNode* front = QueueFront(&q);
		printf("%d ", front->data);
		QueuePop(&q);
		if (front->left)
			QueuePush(&q, front->left);
		if (front->right)
			QueuePush(&q, front->right);
	}
	QueueDestroy(&q);
}

 

•🔥给出两种遍历如何求二叉树?

我们经常会碰到以下这种题型:

这些题通常都是给出其中两种遍历序列(或给出层序遍历序列),求节解这棵二叉树或者它的其他序列 。我们以上面第2题为例,假设我们要求整棵二叉树:

前序遍历:E F H I G J K        中序遍历:H F I E J K G

1.前序遍历确定根

即前序遍历的第一个E即为整棵二叉树的根节点。

2.中序遍历分割左右子树

即在中序遍历中找到E,则E左边的H F I为左子树,右边的J K G为右子树。

由此我们得到一下分析:

 分析到这个地方,我们可以得到左子树不为空,那么前序遍历的第二个F就是左子树的根,进而可以从中序遍历中找到F,它的左边H就是左子树的左子树,右边I就是左子树的右子树。同理,左子树走完后的第一个为G,则G是右子树的根节点,在中序遍历中找到G,它的左边J K就是右子树的左子树,而右子树的右子树为空。那么J K同理可以分析出J为它的左子树,K是J的右子树,即整棵树为:

🍋知识点二:二叉树基本方法

 • 🌰1.二叉树节点个数

我们利用递归的思想,将大问题拆分成许多类似的小问题。二叉树的节点个数等于它左子树的节点个数加上它右子树的节点个数再加1(它自己),而左子树和右子树的节点数同样等于它的左、右子树节点数和它自己,递归到最后节点如果为空,数量为0即可。

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

    

• 🌰2.二叉树叶子节点个数

我们的思想还是不变,都是递归的思想。叶子节点指的是度为0的节点,所以我们要检查一个节点它的左右孩子节点是否为空,如果都为空,那么就是叶子节点,返回1;如果不都为空,就递归下去,等于它的左子树和右子树的叶子节点之和;如果本身就是空,那就是0。

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

    

• 🌰3.二叉树第k层节点个数

第k层的节点个数等于左右子树第k-1层的节点个数之和,同样递归下去,直到k=1,也就是第1层,那就直接返回1。特殊地,如果节点为空,返回0。

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

    

• 🌰4.二叉树的高度

二叉树的高度等于它左右子树高的那一棵子树的高度+1,我们可以记录左右子树的值再进行判断,也可以用fmax函数,如果数为空,返回0。fmax函数在头文件math.h中声明。

int TreeHeight(BTNode* root)
{
	if (root == NULL)
		return 0;
	/*int left = TreeHeight(root->left);
	int right = TreeHeight(root->right);
	return left > right ? right + 1 : left + 1;*/
	return fmax(TreeHeight(root->left), TreeHeight(root->right)) + 1;
}

注意:这里使用三目运算符会使性能大大降低,因为每次return都要更多次地计算子树的高度。

    

• 🌰5.查找值为x的节点

用递归的方式遍历整棵树,如果为空就直接return NULL,不为空判断值是否为x,不为x就继续往下递归,为x就返回。也就是看自己是不是x,不是就返回左子树的查找,左子树也没有就返回右子树的查找。

BTNode* TreeFind(BTNode* root, int x)
{
	if (root == NULL)
		return NULL;
	if (root->data == x)
		return root;
	BTNode* ret = TreeFind(root->left, x);
	if (ret)
		return ret;
	return TreeFind(root->right, x);
}

    

• 🌰6.判断是否为完全二叉树

这时我们依然需要借助队列来进行判断。我们的思路是:将数中的每一个节点按照层序遍历的方式入队列(空节点也入队列),如果进入队列的节点为空,那么就停止,去判断后面所剩下的节点是否都为空。如果都为空,那就是完全二叉树,如果后面有不为空的节点,就不是完全二叉树。

 

代码如下:

bool TreeComplete(BTNode* root)
{
	Queue q;
	QueueInit(&q);
	if (root)
		QueuePush(&q, root);
	while (!QueueEmpty(&q))
	{
		BTNode* front = QueueFront(&q);
		QueuePop(&q);
		if (front == NULL)
		{
			break;
		}
		QueuePush(&q, front->left);
		QueuePush(&q, front->right);
	}
	while (!QueueEmpty(&q))
	{
		BTNode* front = QueueFront(&q);
		QueuePop(&q);
		if (front)
		{
			QueueDestroy(&q);
			return false;
		}
	}
	QueueDestroy(&q);
	return true;
}

    

• 🌰7.二叉树的销毁

销毁儿茶俗话就比较简单了,依次释放左右节点再释放根1节点就可以了。

void TreeDestroy(BTNode* root)
{
	if (root == NULL)
		return;
	free(root->left);
	free(root->right);
	free(root);
}

• ✨SumUp结语

到这里本篇文章的内容就结束了,二叉树比我们以往的数据结构更加抽象,相信大家看完本篇文章已经发现了,涉及到的实现方法不断用到了递归的思想。希望大家可以好好复习今天的内容,自己尝试写代码~

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

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

相关文章

LLM:归一化 总结

一、Batch Normalization 原理 Batch Normalization 是一种用于加速神经网络训练并提高稳定性的技术。它通过在每一层网络的激活值上进行归一化处理&#xff0c;使得每一层的输入分布更加稳定&#xff0c;从而加速训练过程&#xff0c;并且减轻了对参数初始化的依赖。 公式 …

计算机导论速成笔记

【拯救者】计算机导论速成&#xff08;基础真题&#xff09;期末/升本均适用 ©无忌教育 ❀适⽤于所有 计算机导论 课本 目录&#xff1a; 好评&#xff1a; 特色&#xff1a; ①先讲本章考点, ②接着下节就讲对应的考题 观看地址&#xff1a;

太美了!智能汽车触摸屏中控让驾驶员和乘客目不转睛

太美了&#xff01;智能汽车触摸屏中控让驾驶员和乘客目不转睛 引言 艾斯视觉作为行业ui设计和前端开发领域的从业者&#xff0c;其观点始终认为&#xff1a;智能汽车已经成为现代交通的新宠。其中&#xff0c;触摸屏中控系统以其美观、智能、人性化的特点&#xff0c;为驾驶…

GEE:多面板同步缩放查看多源数据,并实现交互选点构建NDVI曲线

一. 目标 ①构建三个面板&#xff0c;分别显示不同来源数据&#xff1b; ②面板1显示哨兵数据面版2显示谷歌高清数据面板3实现用户任意交互选点&#xff0c;并以该点为中心构建正方形&#xff0c;随后生成该正方形的区域NDVI平均值长时序曲线&#xff1b; ③保证前两个面板可…

第 3 章 数据和 C

目录 3.1 示例程序 3.1.1 程序中的新元素 3.2 变量与常量数据 3.3 数据: 数据类型关键字 3.3.1 整数和浮点数 3.3.2 整数 3.3.3 浮点数 3.4 C 语言基本数据类型 3.4.1 int 类型 1. 声明 int 变量 2. 初始化变量 3. int 类型常量 4. 打印 int 值 5. 八进制和十六进…

找工作准备刷题Day8 二叉树 (卡尔41期训练营 7.22)

第一题&#xff1a;Leetcode235. 二叉搜索树的最近公共祖先 题目描述 题解1——递归法 class Solution { public:TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {if (root nullptr)return nullptr;if (root->val > p->val &&…

01、爬虫学习入门

爬虫&#xff1a;通过编写程序&#xff0c;来获取获取互联网上的资源 需求&#xff1a;用程序模拟浏览器&#xff0c;输入一个网址&#xff0c;从该网址获取到资源或内容 一、入门程序 #使用urlopen来进行爬取 from urllib.request import urlopen url "http://www.ba…

网络地址转换-NAT

前言&#xff1a;本博客仅作记录学习使用&#xff0c;部分图片出自网络&#xff0c;如有侵犯您的权益&#xff0c;请联系删除 一、相关知识 网络地址转换&#xff08;Network Address Translation&#xff0c;NAT&#xff09;的基本作用是实现内网 IP 地址与外网 IP 地址的转换…

求职学习day11背八股

本质上我和早睡早上起来玩一早上的游戏没有区别&#xff0c;甚至学习状态更差。得想个办法 12 点睡觉。 第三次 hr 面&#xff0c;为德科和华为的工作效率感到无语。 下周一两场面试。真是要死&#xff0c;这五天时间基本没学什么。八股、项目、手撕。。没一个有能力的感觉。…

代码随想录算法训练营第22天-leetcode-回溯算法part01:

#回溯算法理论基础 能解决的问题&#xff1a; 组合问题&#xff1a;N个数里面按一定规则找出k个数的集合切割问题&#xff1a;一个字符串按一定规则有几种切割方式子集问题&#xff1a;一个N个数的集合里有多少符合条件的子集排列问题&#xff1a;N个数按一定规则全排列&…

[023-2].第2节:SpringBoot中接收参数相关注解

我的后端学习大纲 SpringBoot学习大纲 1.1.基本介绍&#xff1a; SpringBoot接收客户端提交的数据、参数会使用的一些注解&#xff1a; 1.PathVarible2. RequestHeader3.RequestParam4.CookieValue5.RequestBody6.RequestAttribute 1.2.接收参数相关注解与应用实例&#xff1a;…

VirtualBox7.0.18报: IPv6 prefix is not unicast.

VirtualBox7.0.18 报: IPv6 prefix is not unicast. VirtualBox7.0.18 在设置 “NAT网络(Nat Network)” 的IPv6时, 报 IPv6 prefix x::x is not unicast. IPv6 prefix x::x is not unicast. 意思是 x::x 不是IPv6的单播地址 解决办法是: 改为 fcxx 或 fdxx 开头, 如 fc00 ,…

QT Creator下载安装详细教程(保姆级教程)

qt下载安装 1.下载网址 通过清华大学开源软件镜像站进行下载&#xff1a;链接: https://mirrors.tuna.tsinghua.edu.cn/qt/development_releases/online_installers/ 这里我选的是4.4版本的&#xff0c;也可以选择4.7版本&#xff0c;问题不大。 根据电脑系统选择下载linux…

一天一个Harmony Next APP

历史的车轮已经来到太平洋东海岸。 此时如果还不会撸Harmony APP&#xff0c;注定要被碾压。 鸿蒙开发正在如火如荼的开展&#xff0c;抽空看一看吧。 一看就会&#xff0c;一做就废。 这里是一个看废的demo。 主要通过arkts实现鸿蒙关系数据库的封装&#xff0c;实现数据…

搭建自己的金融数据源和量化分析平台(一):系统架构设计

0x00 前言 由于某得的数据实在是太贵&#xff0c;某花顺免费版又不提供专业的数据分析工具&#xff0c;Tushare也开始收费&#xff0c;因此决定基于python和MySQL搭建一个自己的金融数据库。期望做到仅依靠交易所、巨潮资讯网等官方的公开数据实现&#xff0c;尽量不依靠某花顺…

Java中线程池的基本使用

参考这个课程&#xff0c;讲的非常详细 27.线程池使用步骤总结_哔哩哔哩_bilibili 应用场景 商品秒杀 文件上传 订票系统 自定义线程池&#xff08;让我们了解底层逻辑的&#xff09; 构造方法 饱和机制 就是我们的队列饱和后我们该怎么处理 是拒绝这个任务还是啥…

API 技术开发分享:连接电商平台数据获取的桥梁

在当今数字化的时代&#xff0c;API&#xff08;Application Programming Interface&#xff0c;应用程序编程接口&#xff09;技术成为了实现不同系统之间通信和数据交换的关键。它就像是一座无形的桥梁&#xff0c;使得各种应用能够相互协作&#xff0c;共享资源&#xff0c;…

网页突然被恶意跳转或无法打开?DNS污染怎么解决?

前言 在网上冲浪时&#xff0c;我们时常会遭遇DNS污染这一区域性攻击&#xff0c;几乎无人能幸免。受影响时&#xff1a;尝试访问正规网站可能会被错误导向赌博、色情或其他恶意站点。 1.我们为什么需要DNS 当我们想要访问一个网站时&#xff0c;就像拨打朋友的电话号码一样…

第三届人工智能、物联网和云计算技术国际会议(AIoTC 2024,9月13-15)

第三届人工智能、物联网与云计算技术国际会议(AIoTC 2024)将于2024年9月13日-15日在中国武汉举行。 本次会议由华中师范大学伍伦贡联合研究院与南京大学联合主办、江苏省大数据区块链与智能信息专委会承办、江苏省概率统计学会、江苏省应用统计学会、Sir Forum、南京理工大学、…

docker笔记6-数据卷

docker笔记6-数据卷 一、初识dockerfile1.1 编写镜像1.2 构建镜像1.3 运行镜像 二、案例2.1 多个容器共享数据2.2 多个mysql数据共享 一、初识dockerfile 之前的内容中&#xff0c;我们都是通过pull的方式拉取别人写好的镜像&#xff0c;那么通过编写dockerfile&#xff0c;我…