当前位置: 首页 > news >正文

数据结构:详解【树和二叉树】

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 链式存储
二叉树的链式存储结构是指,用链表来表示一棵二叉树,即用链来指示元素的逻辑关系。 通常的方法是链表中每个结点由三个域组成,数据域和左右指针域,左右指针分别用来给出该结点左孩子和右孩子所在的链结点的存储地址

![在这里插入图片描述](https://img-blog.csdnimg.cn/direct/c24bc02ae45b457aba2121bb692246bc.png

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 前序函数递归展开图:
![![在这里插入图片描述](https://img-blog.csdnimg.cn/direct/e81c18c342764d33a3d4497b7a349d03.png](https://img-blog.csdnimg.cn/direct/5021db310f0b46a5a506abe96959af6d.png
中序和后续的递归展开图类似,读者自行分析。

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) );}

递归调用可抽象为:

![在这里插入图片描述](https://img-blog.csdnimg.cn/direct/3b543b40b6fe42139d9ea36bc59e7b5d.png

两种方法的输出结果均是:
![在这里插入图片描述](https://img-blog.csdnimg.cn/direct/c6d745f809604d9ba7e3a7d4f23cc8ca.png
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. 树的概念及结构&#xff08;了解&#xff09; 1.1 树的概念 树是一种非线性的数据结构&#xff0c;它是由n&#xff08;n>0&#xff09;个有限结点组成一个具有层次关系的集合。把它叫做树是因为它看起来像一棵倒挂的树&#xff0c;也就是说它是根朝上&#xff0c;而叶朝…...

“成像光谱遥感技术中的AI革命:ChatGPT在遥感领域中的应用“

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

semhear环境sox

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

如何快速开启一个项目-ApiHug - API design Copilot

ApiHug101-001开启篇 &#x1f917; ApiHug {Postman|Swagger|Api...} 快↑ 准√ 省↓ GitHub - apihug/apihug.com: All abou the Apihug apihug.com: 有爱&#xff0c;有温度&#xff0c;有质量&#xff0c;有信任ApiHug - API design Copilot - IntelliJ IDEs Plugin |…...

从用友U9到钉钉通过接口配置打通数据

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

PyQt qrc2py 使用PowerShell将qrc文件转为py文件并且将导入模块PyQt或PySide转换为qtpy模块开箱即用

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

phpstorm设置头部注释和自定义注释内容

先说设置位置&#xff1a; PhpStorm中文件、类、函数等注释的设置在&#xff1a;setting-》Editor-》FIle and Code Template-》Includes-》PHP Function Doc Comment下设置即可&#xff0c;其中方法的默认是这样的&#xff1a; /** ${PARAM_DOC} #if (${TYPE_HINT} ! "…...

【数据分析面试】10. 计算平均通勤时间(SQL:timestampdiff() 和datediff()区别)

题目 假设你在Uber工作。rides表包含了关于Uber用户在美国各地的行程信息。 编写一个查询&#xff0c;以获取纽约&#xff08;NY&#xff09;每位通勤者的平均通勤时间&#xff08;以分钟为单位&#xff09;&#xff0c;以及纽约所有通勤者的平均通勤时间&#xff08;以分钟为…...

2024年150道高频Java面试题(二十二)

43. ArrayList 和 Vector 的区别是什么&#xff1f; ArrayList 和 Vector 是 Java 中用于存储对象的两种不同类型的动态数组。它们都实现了 List 接口&#xff0c;但存在一些重要的区别&#xff1a; 同步性&#xff1a; ArrayList 是不同步的&#xff0c;意味着它不是线程安全…...

如何使用校园网——Win10笔记本,台式机互开热点

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

c#:简洁实现if-else语句

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

金融贷款批准预测项目

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

FR中隐藏系统管理--用户管理中 表格中每条数据中的编辑按钮,删除按钮

比如隐藏删除按钮&#xff1a; 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++】

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

rust-tokio发布考古

源头&#xff1a; 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医疗图像配准算法

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

设计模式(18):状态模式

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

如果用大模型考公,kimi、通义千问谁能考高分?

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

如何在Java中创建对象输入流

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

Vue 打包或运行时报错Error: error:0308010C

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

React Native 开发环境搭建(全平台详解)

React Native 开发环境搭建&#xff08;全平台详解&#xff09; 在开始使用 React Native 开发移动应用之前&#xff0c;正确设置开发环境是至关重要的一步。本文将为你提供一份全面的指南&#xff0c;涵盖 macOS 和 Windows 平台的配置步骤&#xff0c;如何在 Android 和 iOS…...

vue3 字体颜色设置的多种方式

在Vue 3中设置字体颜色可以通过多种方式实现&#xff0c;这取决于你是想在组件内部直接设置&#xff0c;还是在CSS/SCSS/LESS等样式文件中定义。以下是几种常见的方法&#xff1a; 1. 内联样式 你可以直接在模板中使用style绑定来设置字体颜色。 <template><div :s…...

高危文件识别的常用算法:原理、应用与企业场景

高危文件识别的常用算法&#xff1a;原理、应用与企业场景 高危文件识别旨在检测可能导致安全威胁的文件&#xff0c;如包含恶意代码、敏感数据或欺诈内容的文档&#xff0c;在企业协同办公环境中&#xff08;如Teams、Google Workspace&#xff09;尤为重要。结合大模型技术&…...

Spring Boot面试题精选汇总

&#x1f91f;致敬读者 &#x1f7e9;感谢阅读&#x1f7e6;笑口常开&#x1f7ea;生日快乐⬛早点睡觉 &#x1f4d8;博主相关 &#x1f7e7;博主信息&#x1f7e8;博客首页&#x1f7eb;专栏推荐&#x1f7e5;活动信息 文章目录 Spring Boot面试题精选汇总⚙️ **一、核心概…...

css的定位(position)详解:相对定位 绝对定位 固定定位

在 CSS 中&#xff0c;元素的定位通过 position 属性控制&#xff0c;共有 5 种定位模式&#xff1a;static&#xff08;静态定位&#xff09;、relative&#xff08;相对定位&#xff09;、absolute&#xff08;绝对定位&#xff09;、fixed&#xff08;固定定位&#xff09;和…...

Rapidio门铃消息FIFO溢出机制

关于RapidIO门铃消息FIFO的溢出机制及其与中断抖动的关系&#xff0c;以下是深入解析&#xff1a; 门铃FIFO溢出的本质 在RapidIO系统中&#xff0c;门铃消息FIFO是硬件控制器内部的缓冲区&#xff0c;用于临时存储接收到的门铃消息&#xff08;Doorbell Message&#xff09;。…...

企业如何增强终端安全?

在数字化转型加速的今天&#xff0c;企业的业务运行越来越依赖于终端设备。从员工的笔记本电脑、智能手机&#xff0c;到工厂里的物联网设备、智能传感器&#xff0c;这些终端构成了企业与外部世界连接的 “神经末梢”。然而&#xff0c;随着远程办公的常态化和设备接入的爆炸式…...

【JVM】Java虚拟机(二)——垃圾回收

目录 一、如何判断对象可以回收 &#xff08;一&#xff09;引用计数法 &#xff08;二&#xff09;可达性分析算法 二、垃圾回收算法 &#xff08;一&#xff09;标记清除 &#xff08;二&#xff09;标记整理 &#xff08;三&#xff09;复制 &#xff08;四&#xff…...

MySQL 部分重点知识篇

一、数据库对象 1. 主键 定义 &#xff1a;主键是用于唯一标识表中每一行记录的字段或字段组合。它具有唯一性和非空性特点。 作用 &#xff1a;确保数据的完整性&#xff0c;便于数据的查询和管理。 示例 &#xff1a;在学生信息表中&#xff0c;学号可以作为主键&#xff…...

Cilium动手实验室: 精通之旅---13.Cilium LoadBalancer IPAM and L2 Service Announcement

Cilium动手实验室: 精通之旅---13.Cilium LoadBalancer IPAM and L2 Service Announcement 1. LAB环境2. L2公告策略2.1 部署Death Star2.2 访问服务2.3 部署L2公告策略2.4 服务宣告 3. 可视化 ARP 流量3.1 部署新服务3.2 准备可视化3.3 再次请求 4. 自动IPAM4.1 IPAM Pool4.2 …...