数据结构:详解【树和二叉树】
1. 树的概念及结构(了解)
1.1 树的概念
树是一种非线性的数据结构,它是由n(n>=0)个有限结点组成一个具有层次关系的集合。把它叫做树是因为它看起来像一棵倒挂的树,也就是说它是根朝上,而叶朝下的。
1.2 树的结构
1.3 树与非树:
1.4 树在实际中的运用(表示文件系统的目录树结构)
2. 与树的结构相关的概念
- 节点的度:一个节点含有的子树的个数称为该节点的度; 如上图:A的为6。
- 叶节点或终端节点:度为0的节点称为叶节点; 如上图:B、C、H、I…等节点为叶节点。
- 非终端节点或分支节点:度不为0的节点; 如上图:D、E、F、G…等节点为分支节点。
- 双亲节点或父节点:若一个节点含有子节点,则这个节点称为其子节点的父节点; 如上图:A是B的父节点。
- 孩子节点或子节点:一个节点含有的子树的根节点称为该节点的子节点; 如上图:B是A的孩子节点。
- 兄弟节点:具有相同父节点的节点互称为兄弟节点; 如上图:B、C是兄弟节点。
- 树的度:一棵树中,最大的节点的度称为树的度; 如上图:树的度为6。
- 节点的层次:从根开始定义起,根为第1层,根的子节点为第2层,以此类推;
- 树的高度或深度:树中节点的最大层次; 如上图:树的高度为4。
- 森林:由m(m>0)棵互不相交的多颗树的集合称为森林;(数据结构中的学习并查集本质就是一个森林)。
3. 二叉树的概念及结构
2.1 概念
一棵二叉树是结点的一个有限集合,该集合或者为空,或者是由一个根节点加上两棵别称为左子树和右子树的二叉树组成。
2.2 二叉树的特点:
1.每个结点最多有两棵子树,即二叉树不存在度大于2的结点。
2.二叉树的子树有左右之分,其子树的次序不能颠倒。
2.3 两种特殊的二叉树
(1)满二叉树:
一个二叉树,如果每一个层的结点数都达到最大值,则这个二叉树就是满二叉树。也就是说,如果一个二叉树的层数为K,且结点总数是2k -1 ,则它就是满二叉树。
(2)完全二叉树:
对于深度为K的,有n个结点的二叉树,如果满足前K-1层都是满的,最后一层不满,但最后一层从左到右都是连续的。则这个二叉树就是完全二叉树。
(3)对这两种二叉树的有关数据的推导
4. 二叉树的性质
- 若规定根节点的层数为1,则一棵非空二叉树的第i层上最多有2^(i-1)个结点。
- 若规定根节点的层数为1,则深度为h的二叉树的最大结点数是2^h- 1。
- 对任何一棵二叉树, 如果度为0其叶结点个数为 n0, 度为2的分支结点个数为 n2,则有n0=n2+1。
- 若规定根节点的层数为1,具有n个结点的满二叉树的深度,h = logN(以2为底)。
5. 二叉树的存储
5.1 顺序存储 :
顺序结构存储就是使用数组来存储,一般使用数组只适合表示完全二叉树,因为不是完全二叉树会有空间的浪费。而现实中使用中只有堆才会使用数组来存储,关于堆我们后面的章节会专门讲解。二叉树顺序存储在物理上是一个数组,在逻辑上是一颗二叉树。
5.2 链式存储
二叉树的链式存储结构是指,用链表来表示一棵二叉树,即用链来指示元素的逻辑关系。 通常的方法是链表中每个结点由三个域组成,数据域和左右指针域,左右指针分别用来给出该结点左孩子和右孩子所在的链结点的存储地址 。
6. 二叉树的前,中,后序遍历
要实现前,中,后序遍历,我们需要再来理解二叉树的结构。把任一一棵二叉树分为三部分:根节点,左子树,右子树。
我们这里先拿一棵简单的二叉树举例:
6.1 二叉树的前序(先根)遍历:
根,左子树,右子树。
对应上图为:A B D NULL NULL E NULL NULL C NULL NULL。
6.2 二叉树的中序(中根)遍历:
左子树,根,右子树。
对应上图为:NULL D NULL B NULL E NULL A NULL C NULL。
6.3 二叉树的后序(后根)遍历:
左子树,右子树,根。
对应上图为:NULL NULL D NULL NULL E B NULL NULL C A。
7. 有关二叉树的常用功能的实现
7.1 三序(深度优先)遍历的代码实现
这里我们需要用到分治算法: 分而治之,把大问题分成类似的子问题,子问题再分成子问题……直到子问题不可再分割。实际上就是递归思想。
7.2 根据上图代码实现如下:
#define _CRT_SECURE_NO_WARNINGS #include <stdio.h>
#include <stdlib.h>typedef char BTDataType;//定义二叉树的结构
typedef struct BinaryTreeNode
{BTDataType data;//存放的数据struct BinaryTreeNode* left;//左子树struct BinaryTreeNode* right;//右子树
}BTNode;//前序遍历
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);}void TreeTest()
{//1.开辟节点和初始化BTNode* A = (BTNode*)malloc(sizeof(BTDataType));if (A == NULL){perror("malloc fail\n");return;}A->data = 'A';A->left = NULL;A->right = NULL;BTNode* B = (BTNode*)malloc(sizeof(BTDataType));if (B == NULL){perror("malloc fail\n");return;}B->data = 'B';B->left = NULL;B->right = NULL;BTNode* C = (BTNode*)malloc(sizeof(BTDataType));if (C == NULL){perror("malloc fail\n");return;}C->data = 'C';C->left = NULL;C->right = NULL;BTNode* D = (BTNode*)malloc(sizeof(BTDataType));if (D == NULL){perror("malloc fail\n");return;}D->data = 'D';D->left = NULL;D->right = NULL;BTNode* E = (BTNode*)malloc(sizeof(BTDataType));if (E == NULL){perror("malloc fail\n");return;}E->data = 'E';E->left = NULL;E->right = NULL;//2.链接各个节点A->left = B;A->right = C;B->left = D;B->right = E;//3.进行输出PrevOrder(A);printf("\n");InOrder(A);printf("\n");PostOrder(A);printf("\n");
}int main()
{TreeTest();return 0;
}
输出结果与我们分析的相同:
7.22 前序函数递归展开图:
中序和后续的递归展开图类似,读者自行分析。
7.3 计算一棵二叉树的总节点数
方法 1:遍历递归计数,定义局部变量size,传地址计数
代码实现如下:
void TreeSize(BTNode* root,int *psize)
{if (root == NULL){return;}else{(*psize)++;}TreeSize(root->left, psize);TreeSize(root->right, psize);}void TreeTest()
{......//续上上文的代码和图int Asize = 0;TreeSize(A, &Asize);printf("Asize:%d\n", Asize);int Bsize = 0;TreeSize(B, &Bsize);printf("Bsize:%d\n", Bsize);}
方法2:分治思想,递归
代码实现如下:
int TreeSize(BTNode* root)
{return root == NULL ? 0 : TreeSize(root->left) + TreeSize(root->right) + 1;
}void TreeTest()
{......//续上上文的代码和图printf("Asize:%d\n",TreeSize(A) );printf("Bsize:%d\n",TreeSize(B) );}
递归调用可抽象为:
两种方法的输出结果均是:
7.4 计算一棵二叉树中叶子节点的个数
利用分治思想,后序遍历。
代码实现如下:
int TreeLeafSize(BTNode* root)
{if (root == NULL)return 0;//是叶节点if (root->left == NULL && root->right == NULL)return 1;//左边的叶节点+右边的叶节点return TreeLeafSize(root->left) + TreeLeafSize(root->right);
}void TreeTest()
{......//续上上文的代码和图printf("LeafSize:%d\n",TreeLeafSize(A) );
}
输出结果是:
7.5 计算二叉树的最大深度
利用分治思想,后序遍历,当根节点为NULL时,返回0;当根节点不为NULL时,分解子问题,先求左右子树的深度,该节点的深度 = 左右子树更大的那一个+1。
代码实现如下:
int MaxDepth(BTNode* root)
{if (root == NULL)return 0 ;int leftdepth = MaxDepth(root->left);int rightdepth = MaxDepth(root->right);return leftdepth > rightdepth ? leftdepth + 1 : rightdepth + 1;}void TreeTest()
{......//续上上文的代码和图printf("MaxDepth:%d\n",MaxDepth(A) );
}
输出结果是:
7.6 销毁二叉树
销毁二叉树不能从根节点开始销毁,不然会找不到其他节点。要用后序遍历,先左节点,右节点,最后根节点。
代码实现如下:
void DestoryTree(BTNode* root)
{if (root == NULL)return;DestoryTree(root->left);DestoryTree(root->right);free(root);root = NULL;
}
8. 二叉树的层序(广度优先)遍历
8.1 什么是层序遍历
设二叉树的根节点所在层数为1,层序遍历就是从所在二叉树的根节点出发,首先访问第一层的树根节点,然后从左到右访问第2层上的节点,接着是第三层的节点,以此类推,自上而下,自左至右逐层访问树的结点的过程就是层序遍历。
8.2 层序遍历的代码实现
要实现二叉树的层序遍历,我们需要借助队列的先进先出的特性。其核心思想是:上一层的一个节点出的时候带其下一层的子节点进。
画图解释如下:
代码实现如下:
void LealOrder(BTNode* root)
{Queue q;QueueInit(&q);if (root)QueuePush(&q, root);while (!QueueEmpty(&q)){BTNode* front = QueueFront(&q);//取出队头QueuePop(&q);printf("%c ", front->data);if (front->left)//左不为空,入左节点QueuePush(&q, front->left);if (front->right)//右不为空,入右节点QueuePush(&q, front->right);}printf("\n");QueueDestory(&q);
}void TreeTest()
{......//续上上文的代码和图LealOrder(A);
}
输出结果为:
相关文章:

数据结构:详解【树和二叉树】
1. 树的概念及结构(了解) 1.1 树的概念 树是一种非线性的数据结构,它是由n(n>0)个有限结点组成一个具有层次关系的集合。把它叫做树是因为它看起来像一棵倒挂的树,也就是说它是根朝上,而叶朝…...

“成像光谱遥感技术中的AI革命:ChatGPT在遥感领域中的应用“
遥感技术主要通过卫星和飞机从远处观察和测量我们的环境,是理解和监测地球物理、化学和生物系统的基石。ChatGPT是由OpenAI开发的最先进的语言模型,在理解和生成人类语言方面表现出了非凡的能力。本文重点介绍ChatGPT在遥感中的应用,人工智能…...

semhear环境sox
这里写自定义目录标题 pip list 看到当前环境下已经有sox了怀疑跟torchaudio和torchvision有关,更新了一下:装了torchvisionsox还是找不到 pip list 看到当前环境下已经有sox了 怀疑跟torchaudio和torchvision有关,更新了一下: p…...

如何快速开启一个项目-ApiHug - API design Copilot
ApiHug101-001开启篇 🤗 ApiHug {Postman|Swagger|Api...} 快↑ 准√ 省↓ GitHub - apihug/apihug.com: All abou the Apihug apihug.com: 有爱,有温度,有质量,有信任ApiHug - API design Copilot - IntelliJ IDEs Plugin |…...

从用友U9到钉钉通过接口配置打通数据
从用友U9到钉钉通过接口配置打通数据 接通系统:用友U9 用友U9cloud深耕制造领域十三载,U9cloud在机械、电子、汽配、家具、整车、军工等细分行业有着深厚的积累,尤其是机械、电子和汽配行业,不但打造了多个成熟的产品模式和应用场…...

PyQt qrc2py 使用PowerShell将qrc文件转为py文件并且将导入模块PyQt或PySide转换为qtpy模块开箱即用
前言 由于需要使用不同的qt环境(PySide,PyQt)所以写了这个脚本,使用找到的随便一个rcc命令去转换qrc文件,然后将导入模块换成qtpy这个通用库(支持pyside2-6,pyqt5-6),老版本的是Qt.py(支持pysi…...

phpstorm设置头部注释和自定义注释内容
先说设置位置: PhpStorm中文件、类、函数等注释的设置在:setting-》Editor-》FIle and Code Template-》Includes-》PHP Function Doc Comment下设置即可,其中方法的默认是这样的: /** ${PARAM_DOC} #if (${TYPE_HINT} ! "…...

【数据分析面试】10. 计算平均通勤时间(SQL:timestampdiff() 和datediff()区别)
题目 假设你在Uber工作。rides表包含了关于Uber用户在美国各地的行程信息。 编写一个查询,以获取纽约(NY)每位通勤者的平均通勤时间(以分钟为单位),以及纽约所有通勤者的平均通勤时间(以分钟为…...

2024年150道高频Java面试题(二十二)
43. ArrayList 和 Vector 的区别是什么? ArrayList 和 Vector 是 Java 中用于存储对象的两种不同类型的动态数组。它们都实现了 List 接口,但存在一些重要的区别: 同步性: ArrayList 是不同步的,意味着它不是线程安全…...

如何使用校园网——Win10笔记本,台式机互开热点
当我们使用校园网的时候,往往只能连接一个电脑端,但是又想两个机子同时连接WIFI怎么办呢? 当然,前提条件是你先得其中一台电脑有网络哈 1、打开想开共享热点的电脑的设置 A、点击WIN,再点击设置 2、点击网络和Inte…...

c#:简洁实现if-else语句
c#:简洁实现if-else语句 在C#中,可以使用三元运算符(? :)来简洁地实现if-else语句。其语法格式为: 条件表达式 ? 表达式1 : 表达式2 例如:当条件表达式为真时,返回表达式1的值,否…...

金融贷款批准预测项目
注意:本文引用自专业人工智能社区Venus AI 更多AI知识请参考原站 ([www.aideeplearning.cn]) 在金融服务行业,贷款审批是一项关键任务,它不仅关系到资金的安全,还直接影响到金融机构的运营效率和风险管理…...

FR中隐藏系统管理--用户管理中 表格中每条数据中的编辑按钮,删除按钮
比如隐藏删除按钮: var userTableTools BI.Constants.getConstant("dec.constant.user.table.tools")for(var key in userTableTools){if(key "delete"){var deleteItem userTableTools["delete"]deleteItem.invisible true;}}...

函数重载和引用【C++】
文章目录 函数重载什么是函数重载?函数重载的作用使用函数重载的注意点为什么C可以函数重载,C语言不行? 引用什么是引用?引用的语法引用的特点引用的使用场景引用的底层实现传参时传引用和传值的效率引用和指针的区别 函数重载 什…...

rust-tokio发布考古
源头: Carl Lerche Aug 4, 2016 I’m very excited to announce a project that has been a long time in the making. 我很兴奋地宣布一个酝酿已久的项目。 Tokio is a network application framework for rapid development and highly scalable deployments…...

3D医疗图像配准 | 基于Vision-Transformer+Pytorch实现的3D医疗图像配准算法
项目应用场景 面向医疗图像配准场景,项目采用 Pytorch ViT 来实现,形态为 3D 医疗图像的配准。 项目效果 项目细节 > 具体参见项目 README.md (1) 模型架构 (2) Vision Transformer 架构 (3) 量化结果分析 项目获取 https://download.csdn.net/down…...

设计模式(18):状态模式
核心 用于解决系统中复杂对象的状态转换以及不同状态下行为的封装问题 结构 环境类(Context): 环境类中维护一个State对象,它定义了当前的状态,并委托当前状态处理一些请求; 抽象状态类(State): 用于封装对象的一个特定状态所对应的行为&a…...

如果用大模型考公,kimi、通义千问谁能考高分?
都说大模型要超越人类了,今天就试试让kimi和通义千问做公务员考试题目,谁能考高分? 测评结果再次让人震惊! 问题提干:大小两种规格的盒装鸡蛋,大盒装23个,小盒装16个,采购员小王买了…...

如何在Java中创建对象输入流
在Java中创建对象输入流(ObjectInputStream)通常涉及以下步骤: 获取源输入流:首先,你需要有一个源输入流,它可能来自文件、网络连接或其他任何可以提供字节序列的源。 包装源输入流:接着&#…...

Vue 打包或运行时报错Error: error:0308010C
问题描述: 报错:Error: error:0308010C 报错原因: 主要是因为 nodeJs V17 版本发布了 OpenSSL3.0 对算法和秘钥大小增加了更为严格的限制,nodeJs v17 之前版本没影响,但 V17 和之后版本会出现这个错误…...

222222222222222222222222
欢迎关注博主 Mindtechnist 或加入【Linux C/C/Python社区】一起学习和分享Linux、C、C、Python、Matlab,机器人运动控制、多机器人协作,智能优化算法,滤波估计、多传感器信息融合,机器学习,人工智能等相关领域的知识和…...

微信小程序 电影院售票选座票务系统5w7l6
uni-app框架:使用Vue.js开发跨平台应用的前端框架,编写一套代码,可编译到Android、小程序等平台。 框架支持:springboot/Ssm/thinkphp/django/flask/express均支持 前端开发:vue.js 可选语言:pythonjavanode.jsphp均支持 运行软件…...

C#:用定时器监控定时器,实现中止定时器正在执行的任务,并重启
Windows服务中使用的比较多的是定时器,但这种定时任务有个比较大的毛病:有时会莫名其妙地停止执行(长时间执行不完,假死),必须得手工重启Windows服务才能恢复正常。这个就太麻烦了。 有没有办法来实现定时…...

计算机组成原理 — CPU 的结构和功能
CPU 的结构和功能 CPU 的结构和功能CPU 概述控制器概述CPU 框架图CPU 寄存器控制单元 CU 指令周期概述指令周期的数据流 指令流水概述指令流水的原理影响流水线性能的因素流水线的性能流水线的多发技术流水线结构 中断系统概述中断请求标记和中断判优逻辑中断请求标记 INTR中断…...

npm包安装与管理:深入解析命令行工具的全方位操作指南,涵盖脚本执行与包发布流程
npm,全称为Node Package Manager,是专为JavaScript生态系统设计的软件包管理系统,尤其与Node.js平台紧密关联。作为Node.js的默认包管理工具,npm为开发者提供了便捷的方式来安装、共享、分发和管理代码模块。 npm作为JavaScript世…...

序列化结构(protobuf)实现一个TCP服务器(C++)
Protocol Buffers(protobuf)是一种由Google开发的用于序列化结构化数据的方法,通常用于在不同应用程序之间进行数据交换或存储数据。它是一种语言无关、平台无关、可扩展的机制,可以用于各种编程语言和环境中。 1、首先建立proto文…...

Python中的list()和map() 用法
list() 在Python中,list() 是一个内置函数,用于创建列表(list)对象。它有几个不同的用途,但最常见的是将一个可迭代对象(如元组、字符串、集合或其他列表)转换为一个新的列表。 以下是一些使用…...

公网环境下如何端口映射?
公网端口映射是一种网络技术,它允许将本地网络中的设备暴露在公共互联网上,以便能够从任何地方访问这些设备。通过公网端口映射,用户可以通过互联网直接访问和控制局域网中的设备,而无需在本地网络中进行复杂的配置。 公网端口映射…...

7-36 输入年份和月份
输入一个年份和月份,输出这个月的天数。 输入格式: 输入年份year和月份month,年份和月份中间用一个空格隔开。 输出格式: 输入year年的month月对应的天数。 输入样例: 2000 2输出样例: 29输入样例: 1900 2输出样例: 28输入样例: 1900 6输出样例…...

Linux C++ 023-类模板
Linux C 023-类模板 本节关键字:Linux、C、类模板 相关库函数:getCapacity、getSize 类模板语法 类模板的作用:建立一个通用的类,类中的成员 数据类型可以不具体制定, 用一个虚拟的类型代表语法: templa…...