数据结构-二叉树_堆

news2024/12/25 3:18:00

一.   树的概念

   树在我们的日常生活中随处可见,人们将生活中的树转换成存放数据的树形结构,就成了数据结构中的“树”。

如上图所示,自然界中的树有树根,有树枝,有树叶,当我们将其转换成树形结构时, 只需将其倒转过来,将树根,树枝的岔口,树枝的顶部画一个圆,就成了一个完美的树形结构:

       树型结构是一种非线性的数据结构,它是由N(N>0)个结点组成的一个数据的集合,我们称这种数据结构为“树”。

      树的根部结点称为根结点,根结点是没有前驱结点的,

      除了根结点外,其余结点被分成互不相交的集合,

      每个集合都是结构与树类型相似的子树,每棵子树的根结点有且仅有一个前驱结点,后          继结点可以有0个或多个。

      每棵子树是互不相交的,如果相交则为“图”不为“树”

      除了根结点外,每个结点都只有一个父结点

      假设一棵树有N个结点,那么这条数有N-1条边 

二.   树的相关术语

父结点:如果一个结点有子节点,那么这个结点是子节点的父节点

子结点: 一个结点含有的子树的根结点称为该结点的子结点

结点的度:一个结点有N个子结点,那么这个结点的度为N

树的度:最大的结点的度称为树的度

叶子结点:度为0的结点

分支结点:度不为0的结点

结点的层次:从根开始定义,根为第一层,根的子结点为第二层,以此类推

树的高度:树中结点的最大层次

三.   二叉树的概念

        二叉树是树的一种,也是我们最常用的树形结构,一棵二叉树是结点的一个有限集合,该集合由一个根结点和两棵分别称为“左子树”和“右子树”的二叉树组成。

上图是一棵二叉树,通过上图我们可知二叉树有以下特点:

1.   二叉树的结点的度不大于2

2.   二叉树的子树有左右之分,因此二叉树是有序树

3.   二叉树可以是空树,可以只有根结点,左子树可以为空,右子树可以为空,左右子树均在 

 

四.   满二叉树 

    对于每个结点的度数都达到最大值,我们称这种二叉树为满二叉树。

    一棵树的层数为N,最大结点数为2*N-1,下图是一棵满二叉树,层数为3,最大节点数为7

 五.   完全二叉树

      完全二叉树是由满二叉树引出来的,对于深度为K的,有N个结点的二叉树,当且仅当其每一个结点都与深度为K的满二叉树中编号从1到N的结点一一对应时称为完全二叉树。

注意:满二叉树一定是完全二叉树,但完全二叉树不一定是满二叉树

六.   顺序结构的二叉树

顺序结构的二叉树底层为数组,但是只适合完全二叉树,不然会造成空间的浪费

对于完全二叉树,因为其结点都有连续对应的编号,因此适合用数组存储,数组的下标代替了结点的编号

对于非完全二叉树,如果使用数组来实现,有内存空间的浪费:

七.   顺序结构二叉树的实现(本文以小堆为例)

我们一般用堆来实现顺序结构的数组,堆其实是一种特殊的二叉树,它有着二叉树的性质,也有许多其他的特性,同时堆也是一棵完全二叉树

堆也分为大堆和小堆:

大堆:根结点存放的数据最大,每个父结点都比其子结点要大

小堆:根结点存放的数据最小,每个父结点都比其子结点要小 

 

子结点与父结点的关系: 

对于子结点:   若子结点 i 在该堆中有效,则其父结点对应的序号为:(i-1)/2,当i=0时,无

父结点,因为i=0时为根结点,根结点没有父结点

对于父结点:若父结点的序号为i,  左孩子序号为2*i+1,右孩子序号为:2*i+2,

对于结点数位n的完全二叉树,若2*i+1>=n,则无左孩子,       若2*i+2>=n,则无右孩子

(都大于最大结点数了,不可能还有子结点的,总不能凭空加上去吧)

1.堆的结构定义 

堆的底层结构是数组,和顺序表很相似,其实在定义结构体中也是十分相似的

typedef int HPDataType;
//堆的定义
//堆的底层为数组
typedef struct Heap {
	HPDataType* arr;//指向动态开辟内存的地址
	int size;//堆的有效数据个数
	int capacity;//堆的最大容量
}HP;

 2.堆的初始化

初始化操作与顺序表一致,这里就不再赘述

void HPInit(HP* php) {
	assert(php);
	php->arr = NULL;
	php->size = php->capacity = 0;
}

3.堆的销毁

销毁操作与顺序表一致,不再赘述

void HPDestroy(HP* php) {
	assert(php);
	if (php->arr) {
		free(php->arr);
	}
	php->capacity = php->size = 0;
	php->arr = NULL;
}

4.入堆

       在入堆操作时,是在堆尾进行插入的,在插入前需要检查当前堆的空间是否足够容纳新数据,空间不足的话需要先扩容再进行入堆。

       因为堆分为大堆和小堆,我们在入堆操作后,新数据所在的位置不一定满足堆的性质,这里我们所以我们需要将新数据向上调整,直到新数据所处的位置满足堆的性质,新数据调整完后更新堆的有效个数。

void HPPush(HP* php, HPDataType x) {
	assert(php);
	//检查空间是否不足
	if (php->capacity == php->size) {
		//如果堆空间为0,扩容
		int newcapacity = php->capacity == 0 ? 4 : php->capacity * 2;
		HPDataType* ret = (HPDataType*)realloc(php->arr, newcapacity*sizeof(HPDataType));
		if (ret == NULL) {
			perror("relloc");
			exit(1);
		}
		php->arr = ret;
		php->capacity = newcapacity;
	}
	//在当前位置入堆
	php->arr[php->size] = x;
	AdjustUp(php->arr,php->size);
	//堆的有效个数+1,为什么要到最后才更新堆的有效个数,
	//因为向上调整法需要传递新插入结点的下标,插入堆后下标刚好是size
    //当然更新有效个数后再进行向上调整也是可以的,修改向上调整函数的第二个参数就行
	++ php->size;
}

那么,对入堆数据的向上调整时是如何实现的呢?

5.向上调整法(堆的插入后需要调整)

毫无疑问,入堆的数据成为了某一个结点的子结点,向上调整我们需要知道当前结点的父结点,

利用当前结点在数组所对应的下标,入堆对应的下标其实就是有效数据个数size

新结点入堆后,开始进行向上调整,当前结点与其父结点相比较, 然后更新父结点与子结点,因为我们建立的是小堆,所以当子结点大于父结点时,便不再需要继续向上调整

//交换函数
void Swap(HPDataType* x, HPDataType* y) {
	HPDataType tmp = *x;
	*x = *y;
	*y = tmp;
}

//向上调整算法,子求父: (子-1)/2
void AdjustUp(HPDataType* arr, int child) {
	int parent = (child - 1) / 2;
	while (child > 0) {//确保当前节点 child 有一个有效的父节点可以进行比较和调整。
		if (arr[child] < arr[parent]) {
			Swap(&arr[child], &arr[parent]);//传址调用
		}
		else {
			break;
		}
	}
}

向上调整法图示: 

6.出堆

对于堆而言:出堆的结点是堆顶,也就是树的根结点

       你或许会想:堆的底层结构为数组,那么我们直接将数组下标为0的数据删除就好。那么真的可以这样操作吗?

如下图,假如我们直接将数组下标为0的结点直接出堆,出堆后既不是大堆也不是小堆,而且原本结点的父子关系也错误了

正确的出堆方法:将堆顶的结点与堆底相调换,再删除堆底结点,再让新的堆顶结点向下调整

因为数组最后一位删除后,堆的结构不会发生改变

 

 

堆顶和堆底相交换后,删除新的堆底,然后新的堆顶向下调整,如果子结点比父结点大,则两者向交换,直到子结点比父结点小或者子结点处于非法范围

 

7.向下调整法(堆的删除后需要调整) 

在小堆的出堆后,要进行向下调整的结点毫无疑问是堆顶,向下调整,我们需要知道其最小的子结点,当我们找到最小的子结点后,将最小的子结点与父结点比较,如果子结点比父结点小,则交换,然后更新父结点和子结点。否则不需要向下调整

//向下调整算法,父求子:父*2+ 1/2
void AdjustDown(HPDataType* arr, int parent, int n) {//n 是数组的大小
	int child = parent*2 + 1;//假设左孩子最小
	while (child < n)//孩子不能超过数组的界限
	{
		//找左右孩子中找最小的
		if (child + 1 < n && arr[child] > arr[child + 1])//child + 1 < n检查是否有右孩子,没有右孩子就不进入
		{
			child++;
		}
		if (arr[child] < arr[parent])//父比子大,交换,因为是形成小堆,最小的要在堆顶
		{
			Swap(&arr[child], &arr[parent]);
			parent = child;
			child = parent * 2 + 1;

		}
		else
		{
			break;
		}
	}
}

8.堆的判空

返回堆的有效数据个数即可判断堆是否为空

//判空
bool HPEmpty(HP* php) {
	assert(php);
	return php->size==0;
}

9.取堆顶数据 

//取堆顶
HPDataType HPTop(HP* php)
{
	assert(php && php->size);

	return php->arr[0];
}

八.全码

#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<stdbool.h>
typedef int HPDataType;
//堆的定义
//堆的底层为数组
typedef struct Heap {
	HPDataType* arr;
	int size;
	int capacity;
}HP;
//小堆
//初始化
void HPInit(HP* php) {
	assert(php);
	php->arr = NULL;
	php->size = php->capacity = 0;
}

//销毁
void HPDestroy(HP* php) {
	assert(php);
	if (php->arr) {
		free(php->arr);
	}
	php->capacity = php->size = 0;
	php->arr = NULL;
}

//交换函数
void Swap(HPDataType* x, HPDataType* y) {
	HPDataType tmp = *x;
	*x = *y;
	*y = tmp;
}
//向上调整算法,子求父: (子-1)/2
void AdjustUp(HPDataType* arr, int child) {
	int parent = (child - 1) / 2;
	while (child > 0) {//确保当前节点 child 有一个有效的父节点可以进行比较和调整。
		if (arr[child] < arr[parent]) {
			Swap(&arr[child], &arr[parent]);
		}
		else {
			break;
		}
	}
}

//向下调整算法,父求子:父*2+ 1/2
void AdjustDown(HPDataType* arr, int parent, int n) {//n 是数组的大小
	int child = parent*2 + 1;//假设左孩子最小
	while (child < n)//孩子不能超过数组的界限
	{
		//找左右孩子中找最小的
		if (child + 1 < n && arr[child] > arr[child + 1])//child + 1 < n检查是否有右孩子,没有右孩子就不进入
		{
			child++;
		}
		if (arr[child] < arr[parent])//父比子大,交换,因为是形成小堆,最小的要在堆顶
		{
			Swap(&arr[child], &arr[parent]);
			parent = child;
			child = parent * 2 + 1;

		}
		else
		{
			break;
		}
	}
}

//入堆
void HPPush(HP* php, HPDataType x) {
	assert(php);
	//检查空间是否不足
	if (php->capacity == php->size) {
		//如果堆空间为0,扩容
		int newcapacity = php->capacity == 0 ? 4 : php->capacity * 2;
		HPDataType* ret = (HPDataType*)realloc(php->arr, newcapacity*sizeof(HPDataType));
		if (ret == NULL) {
			perror("relloc");
			exit(1);
		}
		php->arr = ret;
		php->capacity = newcapacity;
	}
	//在当前位置入堆
	php->arr[php->size] = x;
	AdjustUp(php->arr,php->size);
	//堆的有效个数+1,为什么要到最后才更新堆的有效个数,
	//因为向上调整法需要传递新插入结点的下标,插入堆后下标刚好是size
	++ php->size;
	
}

//出堆
void HPPop(HP* php) {
	//出堆,出的是堆顶,但是不能直接删除堆顶,这样堆就混乱了
	//将堆顶元素和顶底交换,然后删除堆底
	//最后向上调整算法向上调整
	assert(php);

	Swap(&php->arr[0], &php->arr[php->size - 1]);

	-- php->size;
	AdjustDown(php->arr, 0, php->size);

}

//判空
bool HPEmpty(HP* php) {
	assert(php);
	return php->size==0;
}

//取堆顶
HPDataType HPTop(HP* php)
{
	assert(php && php->size);

	return php->arr[0];
}

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

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

相关文章

ModBus Pull的详细安装教程

目录 一.导航 二 .安装 三.激活 四.使用 一.导航 modbus poll 和 modbus slave 是两种Modbus协议的软件工具 。 Modbus Poll&#xff1a;Modbus Poll 是一个客户端&#xff08;或主站&#xff09;软件&#xff0c;它允许用户与支持Modbus协议的设备进行通信。 Modbus Sla…

英国毕业论文问卷及采访设计思路解析

在大多数情况下&#xff0c;英国毕业论文都需要学生收集一手资料。而在一手资料的收集过程中&#xff0c;学生可以通过设计试验&#xff0c;观察&#xff0c;问卷和采访等方式从目标人群或者实验人群中收集到所需的一手数据。在本文中&#xff0c;我们将针对商科和教育学之类的…

Linux——echo-tail-重定向符

echo命令 类似printf 输出 反引号 重定向符 > 和 >> > 覆盖 >> 追加 tail命令 查看文件尾部内容&#xff0c;追踪文件最新更改 tail -num 从尾部往上读num行&#xff0c;默认10行 tail -f 持续跟踪

使用 Go 语言与 Redis 构建高效缓存与消息队列系统

什么是 Redis&#xff1f; Redis 是一个开源的内存数据库&#xff0c;支持多种数据结构&#xff0c;包括字符串、列表、集合、哈希和有序集合。由于 Redis 运行在内存中&#xff0c;读写速度极快&#xff0c;常被用于构建缓存系统、实时排行榜、会话存储和消息队列等高并发场景…

论文《OneLLM:One Framework to Align All Modalities with Language》

&#xff08;没有会员只有做100个节点&#xff0c;mindmaster金主爸爸可不可以给我一个会员啊啊啊啊呜呜呜~&#xff09; 欣赏论文的图和表&#xff1a; 表中作者将自己的模型那一行选择灰色作为背景&#xff0c;更加凸显自己的数据&#xff0c;另外对于最好的结果用加粗黑体…

threads_created增加过大?

set global thread_cache_size128; 在my.cnf文件中直接加上thread_cache_size128 原值为58 MySQL中的Threads线程相关指标解读 - SRE Work mysql性能优化(thread_created) - 八戒vs - 博客园 MySQL :: MySQL 8.4 Reference Manual :: 7.1.10 Server Status Variables

基于Vue3+axios+element-plus等技术开发的新闻发布管理系统

新闻发布管理系统是一个基于Vue3piniavue-routeraxioselement-plus等开发的系统&#xff0c;主要功能包括&#xff1a;登录模块、注册模块、新闻分类管理模块、新闻管理模块、个人中心模块&#xff08;包括基本资料、更换头像、重置密码功能&#xff09;等。 代码下载&#xf…

凡事预则立,不预则废

在交易的竞技场上&#xff0c;每位交易员都拥有自己的一套打法&#xff0c;这些独特的交易风格不仅塑造了他们的个人特点&#xff0c;更是他们成功的关键所在。今天采访的Eagle Trader交易员刘先生&#xff0c;就给我一种很稳妥的感觉&#xff0c;那么&#xff0c;刘先生的“稳…

Linux使用Docker部署Paperless-ngx结合内网穿透打造无纸化远程办公

文章目录 前言1. 部署Paperless-ngx2. 本地访问Paperless-ngx3. Linux安装Cpolar4. 配置公网地址5. 远程访问6. 固定Cpolar公网地址7. 固定地址访问 前言 本文主要介绍如何在Linux系统本地部署Paperless-ngx开源文档管理系统&#xff0c;并结合cpolar内网穿透工具解决本地部署…

腾讯云License 相关

腾讯云视立方 License 是必须购买的吗&#xff1f; 若您下载的腾讯云视立方功能模块中&#xff0c;包含直播推流&#xff08;主播开播和主播观众连麦/主播跨房 PK&#xff09;、短视频&#xff08;视频录制编辑/视频上传发布&#xff09;、终端极速高清和腾讯特效功能模块&…

【springboot9734】基于springboot+vue的财务管理系统

作者主页&#xff1a;Java码库 主营内容&#xff1a;SpringBoot、Vue、SSM、HLMT、Jsp、PHP、Nodejs、Python、爬虫、数据可视化、小程序、安卓app等设计与开发。 收藏点赞不迷路 关注作者有好处 文末获取源码 项目描述 随着信息技术和网络技术的飞速发展&#xff0c;人类已进…

2024 kali虚拟机安装教程,分两大步骤,图文讲解(2)

准备工作&#xff1a; 按照图文讲解&#xff08;1&#xff09;搭建好虚拟机&#xff0c;继续以下步骤 2024 kali虚拟机安装教程&#xff0c;分两大步骤&#xff0c;图文讲解&#xff08;1&#xff09;-CSDN博客 正式开始 1.开启&#xff0c;↑ ↓ 方向键&#xff0c;选择第一…

2024双十一有什么值得买?2024年双十一必买清单大全!

双十一购物狂欢节即将到来&#xff0c;这是一年中家电和数码产品打折的绝佳时机。然而&#xff0c;随着产品的多样化&#xff0c;选择一款合适的商品变得越来越困难。今天&#xff0c;我将为大家分享一些在双十一期间值得选购的高品质好物&#xff0c;让我们一起探索这些精选商…

年薪30W的Java程序员都要求熟悉JVM与性能调优!

一、JVM 内存区域划分 1.程序计数器&#xff08;线程私有&#xff09; 程序计数器&#xff08;Program Counter Register&#xff09;&#xff0c;也有称作为 PC 寄存器。保存的是程序当前执行的指令的地址&#xff08;也可以说保存下一条指令的所在存储单元的地址&#xff0…

自动化测试 | 窗口截图

driver.get_screenshot_as_file 是 Selenium WebDriver 的一个方法&#xff0c;它允许你将当前浏览器窗口&#xff08;或标签页&#xff09;的截图保存为文件。这个方法对于自动化测试中的截图验证非常有用&#xff0c;因为它可以帮助你捕获测试执行过程中的页面状态。 以下是…

EcoVadis认证内容有哪些?EcoVadis认证申请流程?

EcoVadis认证是一个国际性的可持续发展评估平台&#xff0c;旨在帮助全球企业和供应链评鉴其在环境、社会和治理&#xff08;ESG&#xff09;方面的表现。该认证框架由法国的检验、认证和检测机构必维集团&#xff08;Bureau Veritas&#xff09;创建&#xff0c;得到了众多跨国…

python34_可变字符串

可变字符串 说明 在 Python 中&#xff0c;字符串属于不可变对象&#xff0c;不支持原地修改&#xff0c;如果需要修改其中的值&#xff0c;智能创建新的字符串对象。 但是&#xff0c;经常我们确实需要原地修改字符串&#xff0c;可以使用 io.StringIO对象或 array 模块impo…

光伏开发:一充一放和两充两放是什么意思?

一充一放 一充一放是指储能设备在一次充电过程中充满电&#xff0c;并在一次放电过程中将电能全部释放。这种模式的原理相对简单&#xff0c;充电时电能转化为化学能或其他形式的能量储存&#xff0c;放电时则将这些能量转化回电能供应给负载。一充一放模式适用于对储能设备充…

国家医保局印发《长期护理保险专家库管理暂行办法》

银发经济『新趋势大数据』 AgeNews 每日银发产业大事件速览 2024-10-10 星期四 AgeClub整理 国家医保局印发《长期护理保险专家库管理暂行办法》 北京出台《关于加强“老老人”服务保障的若干措施》 《2024全景生态流量秋季报告》&#xff1a;茶饮、生鲜市场均迎来70后扩…

【有啥问啥】 群体智能(Swarm Intelligence):从自然到人工智能的深度探索

群体智能&#xff08;Swarm Intelligence&#xff09;&#xff1a;从自然到人工智能的深度探索 什么是群体智能&#xff1f; 群体智能&#xff08;Swarm Intelligence&#xff09;是一个迷人的研究领域&#xff0c;它专注于社会性生物&#xff08;如蚂蚁、蜜蜂、鸟类等&#…