数据结构——二叉树(中)

news2025/5/26 11:09:21

接上一篇,上一篇主要讲解了关于二叉树的基本知识,也是为了接下来讲解关于堆结构和链式二叉树结构打基础,其实无论是堆结构还是链式二叉树结构,都是二叉树的存储结构,那么今天这一篇主要讲解关于堆结构的实现与应用

堆的概念与结构

堆是一种特殊的树形数据结构,它可以被想象成是一个 “有序的金字塔”,并且这个金字塔是基于完全二叉树构建的。(不知道什么是完全二叉树的小伙伴可以先空降到上一篇)

堆的性质:

  • 堆中某个结点的值总是不大于或不小于其父结点的值
  • 堆总是一棵完全二叉树。
  • 最大堆:每个节点的值都大于或等于其子节点的值。可以把它想象成一场比赛,每个 “选手”(节点)都比它的 “下属”(子节点)更厉害。这样一来,处于金字塔顶端(根节点)的就是最厉害的 “选手”。
  • 最小堆:每个节点的值都小于或等于其子节点的值。就好像每个 “选手” 都比它的 “下属” 弱,那么金字塔顶端的就是最弱的 “选手”。

堆的实现 

 现在讲关于堆这个数据结构的实现

堆的初始化、销毁、打印,因为堆的底层结构是数组,所以堆的实现和顺序表、栈有一定的相似性,那么话不多说直接上代码:

typedef int HPDataType;
typedef struct Heap
{
	HPDataType* arr;
	int size;//有效数据元素个数
	int capacity;//空间容量
}Heap;

//堆的初始化
void HeapInit(Heap* php)
{
	assert(php);
	php->arr = NULL;
	php->capacity = php->size = 0;
}

//堆的销毁
void HeapDestroy(Heap* php)
{
	assert(php);
	for (int i = 0; i < php->size - 1; i++)
	{
		free(php->arr[i]);
		php->arr[i] = NULL;
	}
	php->capacity = php->size = 0;
}

//堆的打印
void HeapPrint(Heap* php)
{
	int i = 0;
	for (i = 0; i < php->size; i++)
	{
		printf("%d ", php->arr[i]);
	}
	printf("\n");
}

以上都是堆的一些基础操作,接下来我们上点强度:堆的插入、删除这些操作,和之前一些数据结构会有一些不一样,先来看看插入吧: 

//堆的插入
void HeapPush(Heap* php, HPDataType x)
{
	assert(php);
	if (php->capacity == php->size)
	{
		int newcapacity = php->capacity == 0 ? 4 : 2 * php->capacity;
		HPDataType* tmp = (HPDataType*)realloc(php->arr,sizeof(HPDataType)*newcapacity);
		if (tmp == NULL)
		{
			perror("malloc fail");
			return 1;
		}
		php->arr = tmp;
		php->capacity = newcapacity;
	}
	php->arr[php->size] = x;
	AdjustUp(php->arr, php->size);
	php->size++;
}

//向上调整
void AdjustUp(HPDataType* arr, int child)
{
	int parent = (child - 1) / 2;
	//假设现在是大堆
	while (child > 0)
	{
		//大堆:<
		//小堆:>
		if (arr[parent] < arr[child])
		{
			Swap(&arr[parent], &arr[child]);
			child = parent;
			parent = (child - 1) / 2;
		}
		else
		{
			break;
		}
	}
}

可以看到这里堆的插入操作,除了常规的申请空间以外,还多了一个向上调整的操作,这是为啥呢?这里给大家说明一下:堆的性质是:首先是一颗完全二叉树,更重要的是它要么是一个大堆,要么是一个小堆,当新插入数据时,为了维持堆的这个特性,就要进行向上调整

那么这个向上调整的思想是什么呢?首先就是通过新插入的结点作为孩子结点,去找他的父亲结点,假设现在是要建大堆,拿他的父亲结点和他自己比较,谁大谁往上放,自己大就自己往上放,父结点大就直接跳出循环

这里我们假设是这样一个大堆,如果要插入 9 这个元素,如图所示。

接下来看看堆的删除吧:

//堆的删除
void HeapPop(Heap* php)
{
	assert(!IsEmpty(php));
	Swap(&php->arr[0], &php->arr[php->size - 1]);
	php->size--;
	AdjustDown(php->arr,0, php->size);
}

//向下调整
AdjustDown(HPDataType* arr, int parent, int n)
{
	int child = parent * 2 + 1;
	while (child < n)
	{
		if (child + 1 < n && arr[child] < arr[child + 1])
		{
			child++;
		}
		//假设是大堆
		//大堆:<
		//小堆:>
		if (arr[parent] < arr[child])
		{
			Swap(&arr[parent], &arr[child]);
			parent = child;
			child = parent * 2 + 1;
		}
		else
		{
			break;
		}
	}
}

这里堆的删除指的是删除堆顶的元素,同理,和堆的插入一样,为了维持堆的大堆/小堆的特性,删除了堆顶元素可能破坏了这个特性,所以也需要调整,但这里有所不同,这里是向下调整,其实思路和向上调整差不多,这里博主就不画图了,重点讲一下向上调整和向下调整的区别以及为什么插入要进行向上调整,而删除是向下调整

首先,向下调整算法在这里的效率要比向上调整算法高,因为向下调整算法是可以忽略最后一层结点的,什么意思呢,我们来看哈,我们进行堆的删除操作时,目的是删除堆顶元素,我们让堆顶结点和最后一个叶子结点先交换位置,再把当前最后一个叶子结点删除,最后进行向下调整,我们通过这个根结点,找到他的孩子结点,让他们进行比较,谁小谁往下放,放到倒数第二层的时候,这个当前结点所对应的二叉树就已经是个堆了,所以最后一层就不用再进行向下调整了,这样效率就会高一些。

然后再来讲一下,为什么插入要进行向上调整,而删除是向下调整:因为我们向上调整的算法思想是通过当前结点找其父结点,再不断向上,如果我们用向下调整,那就是通过当前结点找子节点向下交换,而我们进行插入操作的时候,新插入的这个结点本身就是个叶子结点,他已经没有子结点了,所以在堆的插入操作中,向下调整时不适用的,要进行向上调整,反过来堆的删除操作道理也是一样的,这里就不过多赘述了。

以上就是堆的插入、删除操作,接下来还有一些其他操作比较简单,我这里直接上代码了:

//取堆顶数据
HPDataType HeapTop(Heap* php)
{
	assert(!IsEmpty(php));
	return php->arr[0];
}

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

结尾 

好了以上就是关于堆的所有讲解了,觉得博客写的还不错,可以点点赞哦~

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

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

相关文章

02-MySQL 面试题-mk

文章目录 1.mysql 有哪些存储引擎、区别是什么?1.如何定位慢查询?2.SQL语句执行很慢,如何分析?3.索引概念以及索引底层的数据结构4.什么是聚簇索引什么是非聚簇索引?5.知道什么叫覆盖索引嘛 ?6.索引创建原则有哪些?7.什么情况下索引会失效 ?8.谈一谈你对sql的优化的经验…

#include<bits/stdc++.h>

#include<bits/stdc.h> 是 C 中一个特殊的头文件&#xff0c;其作用如下&#xff1a; 核心作用 ​​包含所有标准库头文件​​ 该头文件会自动引入 C 标准库中的几乎全部头文件&#xff08;如 <iostream>、<vector>、<algorithm> 等&#xff09;&…

在企业级部署中如何优化NVIDIA GPU和容器环境配置:最佳实践与常见误区20250414

在企业级部署中如何优化NVIDIA GPU和容器环境配置&#xff1a;最佳实践与常见误区 引言 随着AI和深度学习技术的迅速发展&#xff0c;企业对GPU加速计算的需求愈加迫切。在此过程中&#xff0c;如何高效地配置宿主机与容器化环境&#xff0c;特别是利用NVIDIA GPU和相关工具&…

Spring Boot 项目三种打印日志的方法详解。Logger,log,logger 解读。

目录 一. 打印日志的常见三种方法&#xff1f; 1.1 手动创建 Logger 对象&#xff08;基于SLF4J API&#xff09; 1.2 使用 Lombok 插件的 Slf4j 注解 1.3 使用 Spring 的 Log 接口&#xff08;使用频率较低&#xff09; 二. 常见的 Logger&#xff0c;logger&#xff0c;…

[react]Next.js之自适应布局和高清屏幕适配解决方案

序言 阅读前首先了解即将要用到的两个包的作用 1.postcss-pxtorem 自动将 CSS 中的 px 单位转换为 rem 单位按照设计稿尺寸直接写 px 值&#xff0c;由插件自动计算 rem 值 2.amfe-flexible 动态设置根元素的 font-size&#xff08;即 1rem 的值&#xff09;根据设备屏幕宽度和…

STM32H503CB升级BootLoader

首先&#xff0c;使用SWD接口&#xff0c;ST-LINK连接电脑和板子。 安装SetupSTM32CubeProgrammer_win64 版本2.19。 以下是接线和软件操作截图。

在Apple Silicon上部署Spark-TTS:四大核心库的技术魔法解析!!!

在Apple Silicon上部署Spark-TTS&#xff1a;四大核心库的技术魔法解析 &#x1f680; &#xff08;M2芯片实测&#xff5c;Python 3.12.9PyTorch 2.6.0全流程解析&#xff09; 一、核心库功能全景图 &#x1f50d; 在Spark-TTS的部署过程中&#xff0c;pip install numpy li…

VMWare 16 PRO 安装 Rocky8 并部署 MySQL8

VMWare 16 PRO 安装 Rocky8 并部署 MySQL8 一.Rocky OS 下载1.官网二.配置 Rocky1.创建新的虚拟机2.稍后安装系统3.选择系统模板4.设置名字和位置5.设置大小6.自定义硬件设置核心、运存和系统镜像7.完成三.启动安装1.上下键直接选择安装2.回车安装3.设置分区(默认即可)和 roo…

cursor如何回退一键回退多个文件的修改

当我们使用 Cursor 写代码时&#xff0c;起初可能操作得很顺利&#xff0c;但某次更改或许会让代码变得面目全非。这时候如果没有使用 Git 该怎么办呢&#xff1f;别担心&#xff0c;Cursor 已经为我们考虑到了。 具体的操作如下&#xff1a; 当我们要取消某次操作时&#xf…

基于RV1126开发板的口罩识别算法开发

1. 口罩识别简介 口罩识别是一种基于深度学习的判断人员有没有戴口罩的分类算法&#xff0c;能广泛的用于安防、生产安全等多种场景。本算法先基于人脸检测和人脸标准化获取的标准人脸&#xff0c;然后输入到口罩识别分类算法进行识别。 本人脸检测算法在数据集表现如下所示&am…

PyCharm显示主菜单和工具栏

显示主菜单 新版 PyCharm 是不显示主菜单的&#xff0c;要想显示主菜单和工具栏&#xff0c;则通过 “视图” → “外观” &#xff0c;勾选 “在单独的工具栏中显示主菜单” 和 “工具栏” 即可。 设置工具栏 此时工具栏里并没有什么工具&#xff0c;因此我们需要自定义工具…

Java工程行业管理软件源码 - 全面的项目管理工具 - 工程项目模块与功能一览

工程项目管理系统 Spring CloudSpring BootMybatisVueElementUI前后端分离构建工程项目管理系统 项目背景 随着公司的快速发展&#xff0c;企业人员和经营规模不断壮大。为了提高工程管理效率、减轻劳动强度、提高信息处理速度和准确性&#xff0c;公司对内部工程管理的提升提…

Redis 高可用集群搭建与优化实践

在分布式系统中,缓存技术用于提升性能和响应速度。 Redis 作为一款高性能的键值存储系统,广泛应用于缓存、消息队列和会话管理等场景。随着业务规模的扩大,单机 Redis 的性能和可用性逐渐无法满足需求。 因此,搭建高可用的 Redis 集群可以解决这一问题。我将详细介绍 Red…

【AI大模型】基于阿里百炼大模型进行调用

目录 一、认识阿里云百炼 模型广场 创建自己的模型 二、AI扩图示例 1、开头服务、设置秘钥 2、选择HTTP方式调用流程 3、创建任务请求示例 4、发送http请求提交任务 5、查看任务进度的流程设计 6、后端查看任务进度代码 三、总结 大家好&#xff0c;我是jstart千语…

【神经网络结构的组成】深入理解 转置卷积与转置卷积核

&#x1f308; 个人主页&#xff1a;十二月的猫-CSDN博客 &#x1f525; 系列专栏&#xff1a; &#x1f3c0;《深度学习理论直觉三十讲》_十二月的猫的博客-CSDN博客 &#x1f4aa;&#x1f3fb; 十二月的寒冬阻挡不了春天的脚步&#xff0c;十二点的黑夜遮蔽不住黎明的曙光 …

MyBatis-plus笔记 (上)

简介 [MyBatis-Plus]&#xff08;简称 MP&#xff09;是一个 [MyBatis]的增强工具&#xff0c;在 MyBatis 的基础上只做增强不做改变&#xff0c;为简化开发、提高效率而生。 mybatis-plus总结&#xff1a; 注意&#xff1a;mybatis-puls仅局限于单表操作。 自动生成单表的C…

大模型微调数据集怎么搞?基于easydataset实现文档转换问答对json数据集!

微调的难点之一在与数据集。本文介绍一种将文档转换为问答数据集的方法&#xff0c;超级快&#xff01; 上图左侧是我的原文档&#xff0c;右侧是我基于文档生成的数据集。 原理是通过将文档片段发送给ollama本地模型&#xff0c;然后本地模型生成有关问题&#xff0c;并基于文…

opencv 灰度实验

opencv 灰度实验 1. 最大值法2. 平均值法3. 加权均值法4(直接读取灰度图)cv2.IMREAD_GRAYSCALE5内置将原图转换为灰度图cv2.cvtColor()6 两个极端的灰度值 灰度图与彩色图最大的不同就是&#xff1a;彩色图是由R、G、B三个通道组成&#xff0c;而灰度图只有一个通道&#xff0c…

高性能内存kv数据库Redis(续)

目录 四.主从同步与对象模型 1.Redis 淘汰策略 2.Redis 如何做到 持久化 2.1 redis为什么要实现持久化 2.2fork进程的写时复制机制 2.3大Key的影响 2.4redis做持久化的方式 2.5 aof 2.6 rdb 2.7 redis 持久化方式的优缺点 3.redis里面的高可用体现在哪里&#xff1f; 3.1r…

【指纹浏览器系列-chromium编译】

本文提供了一步一步的指导来帮助读者在Windows环境下成功编译Chromium浏览器。涵盖了系统需求、开发环境搭建、代码下载及构建等关键步骤。 官方编译文档&#xff1a;https://github.com/chromium/chromium/blob/main/docs/windows_build_instructions.md 一、系统要求 一台…