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

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

目录

  ✨说在前面

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

 • 🌰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.rhkb.cn/news/385019.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Apache DolphinScheduler 3.2.2 版本正式发布!

Apache DolphinScheduler 3.2.2 版本正式发布&#xff01; 近日&#xff0c;Apache DolphinScheduler 发布了 3.2.2 版本。此版本主要基于 3.2.1 版本进行了 bug 修复&#xff0c;新增若干特性&#xff0c;并进行了众多改进和 Bug 修复&#xff0c;以及文档修复等。 &#x1…

【前端 08】简单学习js字符串

JavaScript中的String对象详解 在JavaScript中&#xff0c;字符串&#xff08;String&#xff09;是一种非常基础且常用的数据类型&#xff0c;用于表示文本数据。虽然JavaScript中的字符串是原始数据类型&#xff0c;但它们的行为类似于对象&#xff0c;因为JavaScript为字符…

谷粒商城实战笔记-52~53-商品服务-API-三级分类-新增-修改

文章目录 一&#xff0c;52-商品服务-API-三级分类-新增-新增效果完成1&#xff0c;点击Append按钮&#xff0c;显示弹窗2&#xff0c;测试完整代码 二&#xff0c;53-商品服务-API-三级分类-修改-修改效果完成1&#xff0c;添加Edit按钮并绑定事件2&#xff0c;修改弹窗确定按…

vue3-print-nb实现打印pdf分页

安装插件 npm install vue3-print-nb --savevue3 引入 import print from vue3-print-nb // 打印插件 app.use(print)使用 这里使用的是对象配置方式 对象配置方式——在js中定义一个对象&#xff0c;对象中可配置打印区域相关属性&#xff0c;在需要打印的单据内容最外面的…

【Django】在vscode中新建Django应用并新增路由

文章目录 打开一个终端输入新建app命令在app下的views.py内写一个视图app路由引入该视图项目路由引入app路由项目(settings.py)引入app&#xff08;AntappConfig配置类&#xff09;运行项目 打开一个终端 输入新建app命令 python manage.py startapp antapp在app下的views.py内…

MySQL第一阶段:多表查询、事务

继续我的MySQL之旅&#xff0c;继续上篇的DDL、DML、DQL、以及一些约束&#xff0c;该到了多表查询和事务的学习总结&#xff0c;以及相关的案例实现&#xff0c;为未来的复习以及深入的理解做好知识储备。 目录 多表查询 连接查询 内连接 外连接 子查询 事务 事务简介…

为什么用LeSS?

实现适应性 LeSS是一个产品开发的组织系统&#xff0c;旨在最大化一个组织的适应性。关于适应性&#xff08;或者敏捷性&#xff0c;也就是敏捷开发的初衷&#xff09;我们是指优化&#xff1a; 以相对低的成本改变方向的能力&#xff0c;主要是基于通过频繁交付产生的探索。从…

pyuic5将ui文件转换为py文件报错:one input ui-file must be specified;no element found;

ERROR 1 文件命名不规范Solution 1:文件命名不能有空格 ERROR 2未选中ui文件 Solution 2:选中要转换成py 的文件

writing classes ... [xxx of xxxx] 执行时间太长

一、问题展示 二、解决方法 打开设置【File - Settings…】修改堆大小

使用vfbox网关实现modbus opc profinet iec61850等协议间的转换

在当今物联网&#xff08;IoT&#xff09;与工业自动化日益融合的时代背景下&#xff0c;协议转换网关作为连接不同设备与系统之间的桥梁&#xff0c;扮演着至关重要的角色。VFBox协议转换网关&#xff0c;作为这一领域内的佼佼者&#xff0c;以其高效、灵活、可靠的性能&#…

鸿蒙APP架构及开发入门

1.鸿蒙系统 1.1 什么是鸿蒙 鸿蒙是一款面向万物互联时代的、全新的分布式操作系统。 在传统的单设备系统能力基础上&#xff0c;鸿蒙提出了基于同一套系统能力、适配多种终端形态的分布式理念&#xff0c;能够支持手机、平板、智能穿戴、智慧屏、车机、PC、智能音箱、耳机、…

从代码层面熟悉UniAD,开始学习了解端到端整体架构

0. 简介 最近端到端已经是越来越火了&#xff0c;以UniAD为代表的很多工作不断地在不断刷新端到端的指标&#xff0c;比如最近SparseDrive又重新刷新了所有任务的指标。在端到端火热起来之前&#xff0c;成熟的模块化自动驾驶系统被分解为不同的独立任务&#xff0c;例如感知、…

【Django】网上蛋糕商城后台-商品管理

1.商品管理功能 当管理员点击商品管理时&#xff0c;发送服务器请求 path(admin/goods_list/, viewsAdmin.goods_list), # 处理商品列表请求 def goods_list(request):try:type request.GET["type"]except:type 0try:ym request.GET["ym"]except:ym …

基于微信小程序+SpringBoot+Vue的刷题系统(带1w+文档)

基于微信小程序SpringBootVue的刷题系统(带1w文档) 基于微信小程序SpringBootVue的刷题系统(带1w文档) 本系统是将网络技术和现代的管理理念相结合&#xff0c;根据试题信息的特点进行重新分配、整合形成动态的、分类明确的信息资源&#xff0c;实现了刷题的自动化&#xff0c;…

Springboot 多数据源事务

起因 在一个service方法上使用的事务,其中有方法是调用的多数据源orderDB 但是多数据源没有生效,而是使用的primaryDB 原因 spring 事务实现的方式 以 Transactional 注解为例 (也可以看 TransactionTemplate&#xff0c; 这个流程更简单一点)。 入口&#xff1a;ProxyTransa…

电商项目之如何判断线程池是否执行完所有任务

文章目录 1 问题背景2 前言3 4种常用的方法4 代码4.1 isTerminated()4.2 线程池的任务总数是否等于已执行的任务数4.3 CountDownLatch计数器4.4 CyclicBarrier计数器 1 问题背景 真实生产环境的电商项目&#xff0c;常使用线程池应用于执行大批量操作达到高性能的效果。应用场景…

基于Python的房产数据分析系统的设计与实现(源码+lw+部署文档+讲解等)

文章目录&#xff1a; 目录 详细视频演示 设计文档详细参考 技术开发的参考技术栈&#xff01; 2.1 Python语言 2.2 Django框架 2.3 MySQL 2.4 Hadoop介绍 2.5 Scrapy介绍 4.2 系统结构设计 4.3 数据库设计 界面设计与功能实现 5.1系统登录注册实现 5.2管理员模块…

[Meachines] [Easy] Admirer Adminer远程Mysql反向+Python三方库函数劫持权限提升

信息收集 IP AddressOpening Ports10.10.10.187TCP:21,22,80 $ nmap -p- 10.10.10.187 --min-rate 1000 -sC -sV PORT STATE SERVICE VERSION 21/tcp open ftp vsftpd 3.0.3 22/tcp open ssh OpenSSH 7.4p1 Debian 10deb9u7 (protocol 2.0) | ssh-hostkey: | …

Golang零基础入门课_20240726 课程笔记

视频课程 最近发现越来越多的公司在用Golang了&#xff0c;所以精心整理了一套视频教程给大家&#xff0c;这个只是其中的第一部&#xff0c;后续还会有很多。 视频已经录制完成&#xff0c;完整目录截图如下&#xff1a; 课程目录 01 第一个Go程序.mp402 定义变量.mp403 …

微信公众号获取用户openid(PHP版,snsapi_base模式)

微信公众号获取用户openid的接口有2个&#xff1a;snsapi_base、snsapi_userinfo 详情见微信公众号开发文档&#xff1a;https://developers.weixin.qq.com/doc/offiaccount/OA_Web_Apps/Wechat_webpage_authorization.html 本文介绍用PHP方式调用snsapi_base接口获取微信用户…