数据结构(7)—— 二叉树(1)

news2025/6/6 19:14:54

目录

前言

一、 树概念及结构

1.1树的概念

1.2树的相关概念

1.3数的表示

1.二叉树表示

2.孩子兄弟表示法

3.动态数组存储

1.4树的实际应用

二、二叉树概念及结构

2.1概念

2.2特殊的二叉树

1.满二叉树

2. 完全二叉树

2.3二叉树的性质

2.4二叉树的存储结构

1.顺序存储

2.链式存储

三、二叉树的顺序结构实现

3.1二叉树的顺序结构

3.2堆的概念及其结构

3.3堆的实现

3.3.1结构体定义

3.3.2初始化

3.3.3向上调整(大堆)

3.3.4向下调整(大堆)

3.3.5插入

3.3.6删除头

3.3.7获取堆顶元素

3.3.8检查空

3.3.9获取个数

测试

总结


前言

本文介绍二叉树以及堆的认识,以及对于堆的介绍和代码编写。


一、 树概念及结构

1.1树的概念

树是一种非线性的数据结构,由节点(Node)和边(Edge)组成,是由n(n>=0)个有限结点组成一个具有层次关系的集合。树结构具有层次性,通常用于表示具有父子关系的数据。

有一个特殊的结点,称为根结点,根节点没有前驱结点

除根节点外,其余结点被分成M(M>0)个互不相交的集合T1、T2、……、Tm,其中每一个集合Ti(1<= i <= m)又是一棵结构与树类似的子树。每棵子树的根结点有且只有一个前驱,可以有0个或多个后继因此,因此树是递归定义的。

要注意:

树中子树不能相交,就如之前说的子树的根节点有且只有一个前驱一样,除了根节点以外,每一个子节点有且只有一个根结点,子树不能有交集,否则就不是树形结构,一棵树N个节点那么它有N-1条边。

1.2树的相关概念

一颗树中每一个部分都有自己的名字,针对上面的一个数对树的相关概念进行解释:

节点的度:一个节点包含所有子树的个数成为这个节点的度;例如上面的A节点的度就是6。

叶节点或终端节点:度为0的节点;例如上面的LMJK就是叶节点。

双亲节点或父节点:若一个节点含有子节点,那么这个节点就是其子节点的父节点;例如A就是B的父节点。

孩子节点或父节点:一个节点含有的子树的根节点称为该节点的子节点;上面B就是A的孩子节点。

兄弟节点:具有相同父节点的节点称为兄弟节点;B、C就是兄弟节点,它们的共同父节点就是A。

树的度:一棵树中,最大的节点的度称为数的度;上面就是6。

节点的吃层次:从跟开始定义,跟为第1层,跟的子节点为第2层,以此类推;

树的高度或深度:树中节点的最大层次;上面就是4层。

堂兄弟节点:双亲在同一层的节点互为堂兄弟;如上面的E和F就是互为堂兄弟。

节点的祖先:从跟到该节点所经分支上的所有节点;A就是所有节点的祖先。

子孙:以某节点为跟的子树中任一节点都称为该节点的子孙。如上图:所有节点都是A的子孙。

森林:由m(m>0)棵互不相交的数的集合称为森林。

1.3数的表示

树的表示有很多种,这里介绍几种树的表示方法:

1.二叉树表示

二叉树是最基础的树的结构,每一个节点有两个子节点(左子树和右子树),当然还包括一个data用来存储数据。

// 定义二叉树节点结构
struct TreeNode {
    int data;
    struct TreeNode* left;
    struct TreeNode* right;
};

2.孩子兄弟表示法

孩子兄弟表示法是构建普通数的常用方式。

// 多叉树节点结构(孩子兄弟表示法)
struct TreeNode {
    int data;
    struct TreeNode* firstChild; // 第一个孩子节点
    struct TreeNode* nextSibling; // 兄弟节点
};

3.动态数组存储

当然,使用动态数组也可以实现多叉树节点的结构:

// 多叉树节点结构(动态数组)
struct TreeNode {
    int data;
    int childCount;
    struct TreeNode* children[MAX_CHILDREN];
};

1.4树的实际应用

文件系统管理
文件系统的目录结构通常采用树形组织,根目录为顶层节点,子目录和文件作为子节点。这种结构便于用户导航和管理文件。

数据库索引
B树和B+树是数据库索引的核心数据结构,能够高效支持数据的插入、删除和查询操作,保持对数级的时间复杂度。

网络路由
路由器使用前缀树(Trie)存储IP路由表,快速匹配最长前缀路由规则,优化数据包转发效率。

人工智能决策
决策树通过树形分支模拟人类决策过程,每个节点代表一个判断条件,广泛应用于分类和回归任务。

组织结构管理
企业组织架构常表现为树形,CEO为根节点,各部门经理为子节点,清晰反映汇报关系和职责划分。

XML/HTML解析
DOM树将文档表示为节点树,每个标签作为节点,父子关系反映标签嵌套层级,便于程序分析和操作。

游戏开发
场景图使用树结构管理游戏对象,父子节点关系实现坐标系的层级变换,优化渲染效率。

编译器设计
抽象语法树(AST)表示源代码的语法结构,每个节点对应语言构造,支撑语义分析和代码生成。

二、二叉树概念及结构

2.1概念

一颗二叉树,顾名思义,只能有两个度,所以二叉树是一个有限集合,该集合要么为空,要么每一个根节点加上左右子树组成,同时二叉树有左右之分,次序不能颠倒,因此二叉树是有序树。

2.2特殊的二叉树

1.满二叉树

一个二叉树,如果每一个层的结点数都达到最大值,则这个二叉树就是满二叉树。也就是说,如果一个二叉树的层数为K,且结点总数是 ,则它就是满二叉树。

满二叉树

2. 完全二叉树

完全二叉树是效率很高的数据结构,完全二叉树是由满二叉树而引出来的。对于深度为K的,有n个结点的二叉树,当且仅当其每一个结点都与深度为K的满二叉树中编号从1至n的结点一一对应时称之为完全二叉树。 要注意的是满二叉树是一种特殊的完全二叉树。

完全二叉树

2.3二叉树的性质

1. 若规定根节点的层数为1,则一棵非空二叉树的第i层上最多有2^{i-1} 个结点.

2. 若规定根节点的层数为1,则深度为h的二叉树的最大结点数是2^{h-1} .
3. 对任何一棵二叉树, 如果度为0其叶结点个数为 , 度为2的分支结点个数为 ,则有 n0= n2+1
4. 若规定根节点的层数为1,具有n个结点的满二叉树的深度,h=\log (n+1) . (ps: 是log以2为底,n+1为对数)。
5. 对于具有n个结点的完全二叉树,如果按照从上至下从左至右的数组顺序对所有节点从0开始编号,则对于序号为i的结点有:

1. 若i>0,i位置节点的双亲序号:(i-1)/2;i=0,i为根节点编号,无双亲节点

2. 若2i+1<n,左孩子序号:2i+1,2i+1>=n否则无左孩子

3. 若2i+2<n,右孩子序号:2i+2,2i+2>=n否则无右孩子

2.4二叉树的存储结构

二叉树一般有两种存储结构,一种是顺序结构,一种是链式结构。

1.顺序存储

数序存储就是使用数组来存储,一般使用数组只适合表示完全二叉树,因为不是完全二叉树的话,中间有空挡,本身数组就是一段连续的存储地址,那么就会有浪费的空间,而现实中只有堆才会使用数组来存储,二叉树顺序存储本身就是物理上的一个数组,在逻辑上就是一颗二叉树。

2.链式存储

用链表来表示一颗二叉树,用链来指示元素的逻辑关系,通常的方法就是链表中每个节点由三个域组成,数据域和左右指针域,左右指针分别用来给出该节点左孩子和右孩子所在的链节点的存储地址,一般都是二叉链,其中红黑树是用到的三叉链。

三、二叉树的顺序结构实现

3.1二叉树的顺序结构

普通的二叉树是不适合使用数组来进行存储的,因为可能会出现大量的空间浪费,只有完全二叉树才更适合顺序结构存储,现实中我们把堆用顺序结构来存储。

3.2堆的概念及其结构

堆是一种特殊的完全二叉树,分为最大堆和最小堆。最大堆中每个节点的值都大于或等于其子节点的值,最小堆中每个节点的值都小于或等于其子节点的值。堆常用于实现优先队列和堆排序算法,将根节点最大的堆叫做最大堆或大根堆,根节点最小的堆叫做最小堆或小根堆。

小跟堆

 

大根堆

堆的性质

1.堆中某个节点的值总是不大于或不小于其父节点的值;

2.堆总是一颗完全二叉树;

3.3堆的实现

我们通过实现这些:

void HeapInit(HP* php);//初始化
void HeapPush(HP* php, HPDataType x);//尾插
void HeapPop(HP* php);//删除头
void AdjustDown(HPDataType* a, int n, int parent);//向下调整
void AdjustUp(HPDataType* a, int child);//向上调整
HPDataType HeapTop(HP* php);//堆顶元素
bool HeapEmpty(HP* php);//检查空
int HeapSize(HP* php);//堆数据的个数

3.3.1结构体定义

typedef int HPDataType;
typedef struct Heap
{
	HPDataType* a;
	int size;
	int capacity;
}HP;

3.3.2初始化

void HeapInit(HP* php)
{
	assert(php);
	php->a = (HPDataType*)malloc(sizeof(HPDataType)*4);//分配4空间
	if (php->a == NULL)
	{
		perror("malloc fail");
		return;
	}
	php->size = 0;//当前数据个数
	php->capacity = 4;//空间大小
}

3.3.3向上调整(大堆)

//前提是除了child这个位置,前面的数据都构成堆
void AdjustUp(HPDataType* a, int child)
{
	int parent = (child - 1) / 2;
	//while(parent>=0)//虽然可以运行,但是不建议,因为有问题,,但还是可以跑
	while (child>0)
	{
		if (a[child] > a[parent])
		{
			Swap(&a[child], &a[parent]);
			child = parent;
			parent = (child - 1) / 2;
		}
		else
		{
			break;
		}
	}
}

函数通过计算父节点索引 parent = (child - 1) / 2 定位当前节点的父节点。循环条件 child > 0 确保不会越界,避免了使用 parent >= 0 可能导致的潜在问题。当子节点值大于父节点值时,调用 Swap 交换两者位置,并更新子节点和父节点的索引。如果子节点值不大于父节点值,循环终止,堆性质已满足。

3.3.4向下调整(大堆)

//前提左右子树都是大堆或者小堆
//向下调整,取大换小
void AdjustDown(HPDataType* a, int n, int parent)
{
	int child = parent * 2 + 1;
	while (child < n)
	{
		//选出左右孩子中的大的那一个
		if (child+1<n && a[child + 1] > a[child])
		{
			++child;
		}
		if (a[child] > a[parent])
		{
			Swap(&a[child], &a[parent]);
			parent = child;
			child = parent * 2 + 1;
		}
		else
			break;
	}
}

函数接收三个参数:堆数组a、堆的大小n、需要调整的父节点索引parent。初始化时计算左孩子索引:child = parent * 2 + 1。循环条件为当前孩子节点在堆范围内(child < n)。在循环体内,先比较左右孩子节点的大小(确保右孩子存在的情况下),选择较大的孩子节点。如果较大的孩子节点大于父节点,则交换两者位置,并继续向下调整。如果父节点已经大于或等于两个孩子节点,则调整完成,退出循环。

3.3.5插入

void HeapPush(HP* php,HPDataType x)
{
	assert(php);
	if (php->size == php->capacity)
	{
		HPDataType* tmp = (HPDataType*)realloc(php->a,sizeof(HPDataType) * php->capacity*2);
		if (tmp == NULL)
		{
			perror("realloc fail");
			return;
		}
		php->a = tmp;
		php->capacity *= 2;
	}

	php->a[php->size] = x;
	php->size++;

	AdjustUp(php->a, php->size - 1);
}

3.3.6删除头

//删除堆顶才有意义,也就是删除根,不断的筛选大根堆小跟堆的根节点,从而实现排序,挑出最大最小的
void HeapPop(HP* php)
{
	assert(php);
	assert(!HeapEmpty(php));

	//删除数据
	Swap(&php->a[0], &php->a[php->size - 1]);
	php->size--;

	AdjustDown(php->a,php->size,0);
}

3.3.7获取堆顶元素

HPDataType HeapTop(HP* php)
{
	assert(php);
	return php->a[0];
}

3.3.8检查空

bool HeapEmpty(HP* php)
{
	assert(php);
	return php->size == 0;
}

3.3.9获取个数

int HeapSize(HP* php)
{
	assert(php);
	return php->size;
}

测试

void HeapSort(int* a, int n)
{
	//建堆
	//1.向上调整建堆
	for (int i = 1; i < n; ++i)
	{
		AdjustUp(a, i);//模拟插入建堆,降序
	}
}
//排升序不能建小堆,得建大堆
// 排降序应该建小堆
//排升序第一个数排好了,但是剩下的数关系全乱了

int main()
{
	int a[10] = { 2,1,5,7,6,8,0,9,4,3 };//对数组排序
	HeapSort(a, 10);
	return 0;
}


总结

本文介绍了大小堆的创建以及对于一组数据通过大小堆来实现最后的升序和降序排序。

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

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

相关文章

如何使用 Docker 部署grafana和loki收集vllm日志?

环境: Ubuntu20.04 grafana loki 3.4.1 问题描述: 如何使用 Docker 部署grafana和loki收集vllm日志? 解决方案: 1.创建一个名为 loki 的目录。将 loki 设为当前工作目录: mkdir loki cd loki2.将以下命令复制并粘贴到您的命令行中,以将 loki-local-config.yaml …

Kafka入门- 基础命令操作指南

基础命令 主题 参数含义–bootstrap-server连接的Broker主机名称以及端口号–topic操作的topic–create创建主题–delete删除主题–alter修改主题–list查看所有主题–describe查看主题的详细描述–partitions设置分区数–replication-factor设置分区副本–config更新系统默认…

目标检测我来惹1 R-CNN

目标检测算法&#xff1a; 识别图像中有哪些物体和位置 目标检测算法原理&#xff1a; 记住算法的识别流程、解决问题用到的关键技术 目标检测算法分类&#xff1a; 两阶段&#xff1a;先区域推荐ROI&#xff0c;再目标分类 region proposalCNN提取分类的目标检测框架 RC…

lua的笔记记录

类似python的eval和exec 可以伪装成其他格式的文件&#xff0c;比如.dll 希望在异常发生时&#xff0c;能够让其沉默&#xff0c;即异常捕获。而在 Lua 中实现异常捕获的话&#xff0c;需要使用函数 pcall&#xff0c;假设要执行一段 Lua 代码并捕获里面出现的所有错误&#xf…

智能进化论:AI必须跨越的四大认知鸿沟

1. 智能缺口&#xff1a;AI进化中的四大认知鸿沟 1.1 理解物理世界&#xff1a;从像素到因果的跨越 想象一个AI看着一杯倒下的水&#xff0c;它能描述“水滴形状”却无法预测“桌面会湿”。这正是当前AI的典型困境——缺乏对物理世界的因果理解。主流模型依赖海量图像或视频数…

传统足浴行业数字化转型:线上预约平台的技术架构与商业逻辑

上门按摩服务系统开发正成为行业新风口&#xff0c;这绝不是盲目跟风而是实实在在的市场趋势。随着现代人生活节奏加快&#xff0c;时间成本越来越高&#xff0c;传统到店消费模式已经无法满足消费者对便捷服务的需求。我们的团队深耕上门按摩系统开发领域五年&#xff0c;深刻…

从OCR到Document Parsing,AI时代的非结构化数据处理发生了什么改变?

智能文档处理&#xff1a;非结构化数据提出的挑战 在这个时代的每一天&#xff0c;无论是个人处理账单&#xff0c;还是企业处理合同、保险单、发票、报告或成堆的简历&#xff0c;我们都深陷在海量的非结构化数据之中。这类数据不像整齐排列的数据库表格那样规整&#xff0c;…

Inno Setup 安装向导各个页面详解

概览 表中描述了使用Inno Setup生成的安装包在安装过程中各个页面的字段和对应的说明信息。后文会对各个页面的参数做进一步解释说明。 字段说明wpWelcome欢迎页wpLicense许可协议wpPassword密码wpInfoBefore信息wpUserInfo用户信息wpSelectDir选择目标位置wpSelectComponent…

OPENCV重点结构体Mat的讲解

一、Opencv的作用 OpenCV是一个基于Apache2.0许可&#xff08;开源&#xff09;发行的跨平台计算机视觉和机器学习软件库&#xff0c;可以运行在Linux、Windows、Android和Mac OS操作系统上。 它轻量级而且高效——由一系列 C 函数和少量 C 类构成&#xff0c;同时提供了Pytho…

【趣味Html】第11课:动态闪烁发光粒子五角星

打造炫酷的动态闪烁发光粒子五角星效果 前言 在现代Web开发中&#xff0c;视觉效果的重要性不言而喻。今天我们将深入探讨如何使用HTML5 Canvas和JavaScript创建一个令人惊艳的动态闪烁发光粒子五角星效果。这个项目不仅展示了Canvas的强大功能&#xff0c;还涉及了粒子系统、…

CFTel:一种基于云雾自动化的鲁棒且可扩展的远程机器人架构

中文标题&#xff1a; CFTel&#xff1a;一种基于云雾自动化的鲁棒且可扩展的远程机器人架构 英文标题&#xff1a; CFTel: A Practical Architecture for Robust and Scalable Telerobotics with Cloud-Fog Automation 作者信息 Thien Tran, Jonathan Kua, Minh Tran, Hongh…

Educational Codeforces Round 179 (Rated for Div. 2)

CF2111,简单手速场 A. Energy Crystals 贪心&#xff0c;每次最小值会乘2&#xff0c;直接模拟即可&#xff0c;复杂度 O ( log ⁡ n ) O(\log n) O(logn) void solve(){int x;cin>>x;multiset<int> s{0,0,0};int res0;while(*s.begin()<x){int x*s.begin();s…

完成一个可交互的k8s管理平台的页面开发

使用deepseek完成设计一个k8s管理平台&#xff0c;关键词如下&#xff1a; 完成一个可交互的k8s管理平台的页面开发Kubernetes 管理平台页面设计 下面是一个基于现代Web技术的可交互Kubernetes管理平台的页面设计方案&#xff0c;使用React作为前端框架&#xff0c;配合Ant De…

Windows系统工具:WinToolsPlus 之 SQL Server 日志清理

使用软件时提示数据库事务日志已满&#xff0c; 使用WinToolsPlus 数据库页签 先设置 数据源 &#xff0c; 选择 需要清理日志的数据库&#xff0c; 点击 数据库日志清理 即可。 下载地址&#xff1a; http://v.s3.sh.cn/archives/2279.html

在Windows11上安装 Ubuntu WSL

不想安装虚拟机,想在Windows11上运行Linux。网上虽有教程,但是图片明显都是老图,与Windows11还是有些差异。网上缺乏一个齐全的真正的Windows11运行Linux的教程。 一、在Windows上的设置 1. 在window11的搜索框内(所有你找不到的应用都可以用这个搜索功能)&#xff0c;搜索&q…

嵌入式Linux之RK3568

系统烧写镜像。 1、直接使用正点原子官方的updata.img(MIDP) 进入瑞芯微发开工具RKDevTool&#xff0c;选择升级固件&#xff0c;上传到固件&#xff0c;记住这里要进入maskrom模式或者是loader模式&#xff0c;进入该模式之后点击升级即可。 2、烧入自己制作的镜像(单独、一…

JavaScript性能优化实战技术

目录 性能优化核心原则 代码层面优化 加载优化策略 内存管理实践 及时解除事件监听 避免内存泄漏模式 渲染性能调优 使用requestAnimationFrame优化动画 批量DOM操作减少回流 性能监控工具 现代API应用 缓存策略实施 性能优化核心原则 减少资源加载时间 避免阻塞主…

网页前端开发(基础进阶3--Vue)

Vue3 Vue是一款用于构建用户界面的渐进式的JavaScript框架。 Vue由2部分组成&#xff1a;Vue核心包&#xff0c;Vue插件包 Vue核心包包含&#xff1a;声明式渲染&#xff0c;组件系统。 Vue插件包&#xff1a;VueRouter&#xff08;客户端路由&#xff09;&#xff0c;Vuex…

tryhackme——Abusing Windows Internals(进程注入)

文章目录 一、Abusing Processes二、进程镂空三、线程劫持四、DLL注入五、Memory Execution Alternatives 一、Abusing Processes 操作系统上运行的应用程序可以包含一个或多个进程&#xff0c;进程表示正在执行的程序。进程包含许多其他子组件&#xff0c;并且直接与内存或虚…

基于 Alpine 定制单功能用途(kiosk)电脑

前言 故事回到 7 年前, 在网上冲浪的时候发现了一篇介绍使用 Ubuntu 打造 kiosk 单功能用途电脑的文章, 挺好玩的, 就翻译了一下并比葫芦画瓢先后用了 CentOS 7, ArchLinux 进行了实现. 历史文章: 翻译 - 使用Ubutnu14.04和Chrome打造单功能用途电脑(大屏展示电脑) 使用CentOS…