深入理解堆(Heap):一个强大的数据结构


.
专栏:数据结构|Linux|C语言
路漫漫其修远兮,吾将上下而求索
文章目录
- 前言
- 堆的实现
- 基本操作
- 结构体定义
- 初始化堆(HeapInit)
- 销毁堆(HeapDestroy)
- 重要函数
- 交换函数(Swap)
- 上浮调整(UpAdd)
- 下沉调整(DnAdd)
- 重要操作
- 向堆中插入元素(HeapPush)
- 从堆中弹出元素(HeapPop)
- 堆的应用
- 完整代码
- 结语
前言
在计算机科学中,堆(Heap) 是一种非常重要的数据结构,广泛用于各种应用,从数据分析到算法优化,再到系统编程。堆的一个关键特性是其能够快速找到一组数中的最大或最小值。但是,什么是堆?如何在实际编程中实现和使用堆呢?
堆的实现
堆是一种特殊的完全二叉树。在一个最小堆中,每个父节点的值都小于或等于其子节点的值;相反,在一个最大堆中,每个父节点的值都大于或等于其子节点的值。这种属性使得堆能够快速访问、添加和删除其最大或最小元素。


基本操作
结构体定义
首先,堆是通过以下结构体定义的:
typedef struct Heap
{HPDataType* a; // 指向堆数组的指针size_t size; // 堆当前的大小int capacity; // 堆的最大容量
} Hp;
这里,HPDataType* a 是一个指向动态分配数组的指针,该数组用于存储堆中的元素。size 表示堆中当前元素的数量,而 capacity是数组的最大容量。
初始化堆(HeapInit)
初始化函数 HeapInit 用于设置堆的初始状态:
// 初始化堆
void HeapInit(Hp* hp)
{assert(hp); // 确保堆指针有效hp->a = NULL; // 堆数组指针置空hp->size = 0; // 初始化堆大小为0hp->capacity = 0; // 初始化堆容量为0
}
此函数确保堆的开始状态为空,没有分配任何内存,且大小和容量都为零。
销毁堆(HeapDestroy)
为了避免内存泄漏,当堆不再需要时,应该使用 HeapDestroy 函数来释放其占用的内存:
// 销毁堆
void HeapDestroy(Hp* hp)
{assert(hp); // 确保堆指针有效free(hp->a); // 释放堆数组内存hp->a = NULL; // 将堆数组指针置空hp->size = hp->capacity = 0; // 重置堆大小和容量为0
}
这个函数释放了堆数组 a 的内存,并将指针置空,同时重置大小和容量。
重要函数
交换函数(Swap)
Swap 函数用于交换堆中两个元素的值。这在上浮和下沉调整中非常重要。
// 交换两个元素的值
void Swap(HPDataType* p1, HPDataType* p2)
{HPDataType tmp = 0; // 临时变量用于交换tmp = *p1;*p1 = *p2;*p2 = tmp;
}
上浮调整(UpAdd)
这个函数通过一个临时变量
tmp实现了两个元素值的交换。当一个新元素被加入到堆中时,它可能会破坏堆的特性(在最小堆中,所有父节点的值都应该小于其子节点的值)。UpAdd函数通过将新加入的元素与其父节点进行比较和交换来修复这种情况,直到堆的特性得到恢复或元素到达根位置。
// 上浮调整
void UpAdd(HPDataType* a, HPDataType child)
{int parent = (child - 1) / 2; // 找到父节点while (child > 0){if (a[child] < a[parent]) // 如果子节点小于父节点{Swap(&a[child], &a[parent]); // 交换两者child = parent; // 更新子节点和父节点的位置parent = (child - 1) / 2;}else{break; // 如果不需要交换,则终止循环}}
}
这个过程被称为“上浮”,因为较小的元素(在最小堆中)浮向堆的顶部。

下沉调整(DnAdd)
与上浮调整相反,当堆顶元素被移除后,我们需要从堆的顶部开始将新的根元素下移,以保持堆的特性。这是通过将父节点与其子节点比较并在必要时进行交换来实现的。
// 下沉调整
void DnAdd(HPDataType* a, HPDataType parent, int size)
{int child = parent * 2 + 1; // 找到左子节点while (child < size){if (child + 1 < size && a[child + 1] < a[child]) //检查右子节点是否存在,以及比较左右两个子节点的值{child++; // 选择较小的子节点}if (a[child] < a[parent]){Swap(&a[child], &a[parent]); // 交换父子节点parent = child; // 更新父子节点的位置child = parent * 2 + 1;}else{break; // 如果不需要交换,则终止循环}}
}
这个过程被称为“下沉”,因为较大的元素(在最小堆中)下沉到堆的底部。

重要操作
向堆中插入元素(HeapPush)
插入操作是堆的核心功能之一。
HeapPush函数首先检查是否需要扩展堆的容量,然后将新元素添加到堆的末尾,并进行上浮调整以保持堆的特性:
// 向堆中插入元素
void HeapPush(Hp* hp, HPDataType x)
{assert(hp); // 确保堆指针有效if (hp->size == hp->capacity){// 如果堆已满,扩大容量int newcapacity = hp->capacity == 0 ? 4 : hp->capacity * 2;HPDataType* tmp = (HPDataType*)realloc(hp->a, sizeof(HPDataType) * newcapacity);if (tmp == NULL){perror("realloc fail"); // 内存分配失败exit(-1);}hp->a = tmp;hp->capacity = newcapacity;}hp->a[hp->size] = x; // 插入元素hp->size++; // 堆大小增加UpAdd(hp->a, hp->size - 1); // 上浮调整
}
从堆中弹出元素(HeapPop)
HeapPop函数移除堆顶元素,这通常是堆中的最小或最大值。在移除元素后,它执行下沉调整以保持堆的特性:
// 从堆中弹出元素
void HeapPop(Hp* hp)
{assert(hp); // 确保堆指针有效assert(hp->size > 0); // 确保堆非空Swap(&hp->a[0], &hp->a[hp->size - 1]); // 交换堆顶和堆底元素hp->size--; // 减小堆大小DnAdd(hp->a, 0, hp->size); // 下沉调整
}
上浮和下沉调整
上浮(UpAdd)和下沉(DnAdd)调整是维护堆特性的关键操作。上浮调整用于在添加新元素后恢复堆的特性,而下沉调整则在移除顶部元素后用于恢复堆的特性。
堆的应用
堆在多种场景中都非常有用。例如,在优先队列、堆排序、图算法(如Dijkstra的最短路径算法)中,堆结构都扮演着核心角色。它能够优化这些算法的性能,特别是在处理大数据集时。
如何使用堆?
使用堆的一个典型例子是维护动态数据集的最小或最大元素。以下是使用C语言实现的堆如何工作的一个简单示例:
int main() {int a[] = {4, 6, 2, 1, 5, 8, 2, 9};Hp hp;HeapInit(&hp);// 将数组元素插入堆中for (int i = 0; i < sizeof(a) / sizeof(int); ++i) {HeapPush(&hp, a[i]);}// 弹出并打印堆顶元素,直到堆为空while (!HeapEmpty(&hp)) {printf("%d ", HeapTop(&hp));HeapPop(&hp);}return 0;
}
也可以用堆输出数组a中最大的前三个数
int k = 3;while (k--){printf("%d\n", HeapTop(&hp));HeapPop(&hp);}
完整代码
可以来我的github参观参观,看完整代码
路径点击这里–>数据结构堆的实现练习
结语
希望这篇文章能够帮助您清晰地理解堆的结构和功能,并鼓励您在未来的项目中尝试和探索它。理解数据结构的关键不仅在于记住它们的运作方式,而且在于知道何时以及如何使用它们。无论您是一位经验丰富的程序员还是刚开始编程之旅的新手,掌握数据结构总是一项宝贵的技能。因此,拿起您的编程工具,开始构建、优化,最重要的是,享受编程带来的无限可能吧!
相关文章:
深入理解堆(Heap):一个强大的数据结构
. 个人主页:晓风飞 专栏:数据结构|Linux|C语言 路漫漫其修远兮,吾将上下而求索 文章目录 前言堆的实现基本操作结构体定义初始化堆(HeapInit)销毁堆(HeapDestroy) 重要函数交换函数(…...
抖音在线查权重系统源码,附带查询接口
抖音权重在线查询只需输入抖音主页链接,即可查询作品情况。 搭建教程 上传源码并解压 修改数据库“bygoukai.sql” 修改“config.php” 如需修改水印请修改第40行 如需修改限制次数,请修改第156行 访问域名user.php即可查看访问用户,停…...
Spring Framework和SpringBoot的区别
目录 一、前言 二、什么是Spring 三、什么是Spring Framework 四、什么是SpringBoot 五、使用Spring Framework构建工程 六、使用SpringBoot构建工程 七、总结 一、前言 作为Java程序员,我们都听说过Spring,也都使用过Spring的相关产品࿰…...
2024--Django平台开发-Django知识点(三)
day03 django知识点 项目相关路由相关 urls.py视图相关 views.py模版相关 templates资源相关 static/media 1.项目相关 新项目 开发时,可能遇到使用其他的版本。虚拟环境 老项目 打开项目虚拟环境 1.1 关于新项目 1.系统解释器命令行【学习】 C:/python38- p…...
Github 2024-01-08开源项目周报 Top14
根据Github Trendings的统计,本周(2024-01-08统计)共有14个项目上榜。根据开发语言中项目的数量,汇总情况如下: 开发语言项目数量Python项目5TypeScript项目3C项目2Dart项目1QML项目1Go项目1Shell项目1Rust项目1JavaScript项目1C#项目1 免费…...
vue3 的内置组件汇总
官方给出的说明: Fragment: Vue 3 组件不再要求有一个唯一的根节点,清除了很多无用的占位 div。Teleport: 允许组件渲染在别的元素内,主要开发弹窗组件的时候特别有用。Suspense: 异步组件,更方便开发有异步请求的组件。 一、fr…...
ARM工控机Node-red使用教程
嵌入式ARM工控机Node-red安装教程 从前车马很慢书信很远,而现在人们不停探索“科技改变生活”。 智能终端的出现改变了我们的生活方式,钡铼技术嵌入式工控机协助您灵活布建能源管理、大楼自动化、工业自动化、电动车充电站等各种多元性IoT应用ÿ…...
Visual Studio 发布程序自动更新 ClickOnce和AutoUpdater测试
文章目录 前言运行环境ClickOnce(Visual Studio 程序发布)IIS新建文件夹C# 控制台测试安装测试更新测试卸载 AutoUpdaterDotNET实现原理简单使用新建一个WPF项目 代码封装自动更新代码封装简单使用 总结 前言 虽然写的大部分都是不联网项目,…...
Codeforces Round 761 (Div. 2) E. Christmas Chocolates(思维题 树的直径 二进制性质 lca)
题目 n(n<2e5)个值,第i个值ai(0<ai<1e9),所有ai两两不同 初始时,选择两个位置x,y(x≠y),代表需要对这两个位置进行操作,要把其中一个值变成另一个 你可以执行若干次操作,每一次,你可…...
知识图谱之汽车实战案例综述与前瞻分析
知识图谱的前置介绍 什么是知识图谱 知识图谱本质(Knowledge Graph)上是一种叫做语义网络(semantic network ) 的知识库,即具有有向图结构的一个知识库;图的结点代表实体(entity)或者概念(con…...
网关Gateway
什么是网关? 网关实质上是一个网络通向其他网络的 IP 地址,是当前微服务项目的"统一入口"。 网关能做什么? 反向代理 、鉴权、 流量控制、 熔断、 日志监控等 图片原文:http://t.csdnimg.cn/SvUJh 核心概念 Router(…...
java 生成一个当前时间的时间搓
开发过程中 用时间搓数值格式存储 会更加精准 那么 我们在一些日常增删查改中就可以用时间搓来记录操作时间 就一行代码 long timestamp System.currentTimeMillis();他就能生成当前时间的时间搓 运行结果如下 然后 我们可以在 http://shijianchuo.wiicha.com/ 上进行转换查…...
金融中IC和IR的定义
当谈到金融领域时,IC(Information Coefficient)和IR(Information Ratio)通常是用来评估投资组合管理绩效的指标。它们都涉及到投资者对信息的利用和管理的效果。 信息系数(IC - Information Coefficient&a…...
Git(2):Git环境的安装
本教程里的git命令例子都是在Git Bash中演示的,会用到一些基本的linux命令,在此为大家提前列举: ls/ll 查看当前目录cat 查看文件内容touch 创建文件vi vi编辑器(使用vi编辑器是为了方便展示效果,学员可以记事本、edi…...
Pytest单元测试系列[v1.0.0][pytest插件常用技巧]
使用pytest-xdist并发执行测试 pytest-xdist:Run Tests in Parallel [https://pypi.python.org/pypi/pytest-xdist] 在自动化测试中有些资源只能同时被一个测试用例访问,如果不需要同时使用同一个资源,那么测试用例便可以并行执行 执行命令…...
嵌入式培训机构四个月实训课程笔记(完整版)-Linux系统编程第五天-Linux消息共享内存练习题(物联技术666)
更多配套资料CSDN地址:点赞+关注,功德无量。更多配套资料,欢迎私信。 物联技术666_嵌入式C语言开发,嵌入式硬件,嵌入式培训笔记-CSDN博客物联技术666擅长嵌入式C语言开发,嵌入式硬件,嵌入式培训笔记,等方面的知识,物联技术666关注机器学习,arm开发,物联网,嵌入式硬件,单片机…...
04set注入专题/简单类型/数组/List/Set/Map/空字符串/null/特殊符号
1.1注入外部Bean 在之前使用的案例就是注入外部Bean的方式。 <!-- class属性声明要管理哪个类中的对象 property标签的name是提示set方法名ref标签指明注入的bean的id--><bean id"userServiceBean" class"com.powernode.spring6.service.UserService…...
Linux引导和服务管理
目录 一.Linux引导: 1、Linux开机启动的完整过程: 2、bios的作用: 3、boot: 4.mbr: 5、grub: 6、加载内核文件: 7、启动进程: 8、centos6与centos7的区别: 9、完整的过程 …...
HarmonyOS 应用开发学习笔记 ets自定义组件及其引用 @Component自定义组件
Component注解的作用是用来构建自定义组件 Component组件官方文档 自定义组件具有以下特点: 可组合:允许开发者组合使用系统组件、及其属性和方法。 可重用:自定义组件可以被其他组件重用,并作为不同的实例在不同的父组件或容器…...
在做题中学习(43):长度最小的子数组
LCR 008. 长度最小的子数组 - 力扣(LeetCode) 解法:同向双指针-------滑动窗口算法 解释:本是暴力枚举做法,因为全部是正整数,就可以利用单调性和双指针解决问题来节省时间 思路: 如上面图&am…...
java 实现excel文件转pdf | 无水印 | 无限制
文章目录 目录 文章目录 前言 1.项目远程仓库配置 2.pom文件引入相关依赖 3.代码破解 二、Excel转PDF 1.代码实现 2.Aspose.License.xml 授权文件 总结 前言 java处理excel转pdf一直没找到什么好用的免费jar包工具,自己手写的难度,恐怕高级程序员花费一年的事件,也…...
【Redis技术进阶之路】「原理分析系列开篇」分析客户端和服务端网络诵信交互实现(服务端执行命令请求的过程 - 初始化服务器)
服务端执行命令请求的过程 【专栏简介】【技术大纲】【专栏目标】【目标人群】1. Redis爱好者与社区成员2. 后端开发和系统架构师3. 计算机专业的本科生及研究生 初始化服务器1. 初始化服务器状态结构初始化RedisServer变量 2. 加载相关系统配置和用户配置参数定制化配置参数案…...
条件运算符
C中的三目运算符(也称条件运算符,英文:ternary operator)是一种简洁的条件选择语句,语法如下: 条件表达式 ? 表达式1 : 表达式2• 如果“条件表达式”为true,则整个表达式的结果为“表达式1”…...
鸿蒙中用HarmonyOS SDK应用服务 HarmonyOS5开发一个医院挂号小程序
一、开发准备 环境搭建: 安装DevEco Studio 3.0或更高版本配置HarmonyOS SDK申请开发者账号 项目创建: File > New > Create Project > Application (选择"Empty Ability") 二、核心功能实现 1. 医院科室展示 /…...
【ROS】Nav2源码之nav2_behavior_tree-行为树节点列表
1、行为树节点分类 在 Nav2(Navigation2)的行为树框架中,行为树节点插件按照功能分为 Action(动作节点)、Condition(条件节点)、Control(控制节点) 和 Decorator(装饰节点) 四类。 1.1 动作节点 Action 执行具体的机器人操作或任务,直接与硬件、传感器或外部系统…...
ElasticSearch搜索引擎之倒排索引及其底层算法
文章目录 一、搜索引擎1、什么是搜索引擎?2、搜索引擎的分类3、常用的搜索引擎4、搜索引擎的特点二、倒排索引1、简介2、为什么倒排索引不用B+树1.创建时间长,文件大。2.其次,树深,IO次数可怕。3.索引可能会失效。4.精准度差。三. 倒排索引四、算法1、Term Index的算法2、 …...
【学习笔记】深入理解Java虚拟机学习笔记——第4章 虚拟机性能监控,故障处理工具
第2章 虚拟机性能监控,故障处理工具 4.1 概述 略 4.2 基础故障处理工具 4.2.1 jps:虚拟机进程状况工具 命令:jps [options] [hostid] 功能:本地虚拟机进程显示进程ID(与ps相同),可同时显示主类&#x…...
selenium学习实战【Python爬虫】
selenium学习实战【Python爬虫】 文章目录 selenium学习实战【Python爬虫】一、声明二、学习目标三、安装依赖3.1 安装selenium库3.2 安装浏览器驱动3.2.1 查看Edge版本3.2.2 驱动安装 四、代码讲解4.1 配置浏览器4.2 加载更多4.3 寻找内容4.4 完整代码 五、报告文件爬取5.1 提…...
LangChain知识库管理后端接口:数据库操作详解—— 构建本地知识库系统的基础《二》
这段 Python 代码是一个完整的 知识库数据库操作模块,用于对本地知识库系统中的知识库进行增删改查(CRUD)操作。它基于 SQLAlchemy ORM 框架 和一个自定义的装饰器 with_session 实现数据库会话管理。 📘 一、整体功能概述 该模块…...
招商蛇口 | 执笔CID,启幕低密生活新境
作为中国城市生长的力量,招商蛇口以“美好生活承载者”为使命,深耕全球111座城市,以央企担当匠造时代理想人居。从深圳湾的开拓基因到西安高新CID的战略落子,招商蛇口始终与城市发展同频共振,以建筑诠释对土地与生活的…...
