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

【数据结构】堆(Heap)详解

 在深入了解堆这一重要的数据结构之前,不妨先回顾一下我之前的作品 ——“二叉树详解”。

上篇文章👉剖析二叉树(Binary Tree)

 二叉树作为一种基础的数据结构,为我们理解堆以及其他更复杂的数据结构奠定了坚实的基础。它的结构特点、遍历方式以及在不同场景下的应用,都与堆有着千丝万缕的联系。通过对二叉树的深入学习,能更好地帮助我们掌握堆的相关知识。


目录

一、引言

二、堆的基础概念

(一)完全二叉树与满二叉树

(二)堆的定义与性质

三、堆的操作

(一)堆的插入

(二)堆的删除

(三)最大堆

(四)最小堆

四、堆的应用

(一)堆排序

(二)优先队列

五、总结


一、引言

堆是计算机科学中一种非常重要的数据结构,它在众多算法和应用中发挥着关键作用。本文将深入探讨堆的相关概念、操作及其应用。

二、堆的基础概念

(一)完全二叉树与满二叉树

       1.完全二叉树

  • 若二叉树的深度为 h,则除第 h 层外,其他层的结点全部达到最大值,且第 h 层的所有结点都集中在左子树。 

        2.满二叉树

  • 满二叉树是一种特殊的完全二叉树,所有层的结点都是最大值

 

(二)堆的定义与性质

        1.堆的定义

  • 堆(英语:heap)是计算机科学中一类特殊的数据结构的统称。堆通常是一个可以被看做一棵树的数组对象。
  • n 个元素的序列 \left \{ k_{1},k_{2},k_{i},\cdot\cdot\cdot ,k_{n}\right \} 当且仅当满足下关系时,称之为堆:
  • 堆分为大顶堆小顶堆。在大顶堆中,父节点的值总是大于或等于子节点的值;在小顶堆中,父节点的值总是小于或等于子节点的值

         2.堆的性质

  • 堆中某个节点的值总是不大于或不小于其父节点的值。

  • 堆总是一棵完全二叉树。

  • 将根节点最大的堆叫做最大堆或大根堆,根节点最小的堆叫做最小堆或小根堆。常见的堆有二叉堆、斐波那契堆等。

  • 注意:在二叉树中,若当前节点的下标为,则其父节点的下标为i/2,其左子节点的下标为i*2,其右子节点的下标为i*2+1

三、堆的操作

(一)堆的插入

        1.插入过程

  • 每次插入都是将先将新数据放在数组最后。由于从这个新数据的父结点到根结点必然为一个有序的序列,现在的任务是将这个新数据插入到这个有序序列中,类似于直接插入排序中将一个数据并入到有序区间中。
  • 例如,将数字16插入到堆中,堆的数组是\left [ 10,7,2,5,1\right ]
    • 第一步是将新的元素插入到数组的尾部,数组变成,相应的树变成了:\left [ 10,7,2,5,1,16 \right ]
       107  25 1 16
  • 此时堆属性不满足,因为2在的16上面(这是一个最大堆),需要交换和。得到:
       107 165 1 2
  • 现在还未完成,因为 10 也比 16 小。继续交换插入元素和它的父节点,直到父节点比它大或者到达树的顶部。这就是所谓的 shift-up,每一次插入操作后都需要进行,它将一个太大或者太小的数字 “浮起” 到树的顶部。
  • 最后得到的堆:
       167 105 1 2
  • 插入操作的 C 语言代码示例
void insertHeap(int heap[], int *size, int value) {heap[*size] = value;int index = *size;//将孩子标为index(*size)++;while (index > 0) //将孩子调整到正确位置{int parentIndex = (index - 1) / 2;if (heap[index] > heap[parentIndex]) //如果孩子比父亲大,则交换{int temp = heap[index];heap[index] = heap[parentIndex];heap[parentIndex] = temp;index = parentIndex;//再次与其父亲比较直到break}else break;//孩子比父亲小则退出}
}

(二)堆的删除

        1.删除过程

  • 堆中每次都只能删除堆顶元素。为了便于重建堆,实际的操作是1.将最后一个数据的值赋给根结点,2.然后再从根结点开始进行一次从上向下的调整。3.调整时先在左右子结点中找最小的(对于最大堆),如果父结点比这个最小的子结点还小说明不需要调整了,反之将父结点和它交换后再考虑后面的结点。相当于根结点数据的 “下沉” 过程。
  • 例如,删除树中的10
           167 105 1 2
    • 顶部有一个空的节点,取出数组中的最后一个元素 1,将它放到树的顶部。
    • 然后进行 shift-down 操作。为了保持最大堆的堆属性,需要树的顶部是最大的数据。现在有两个数字 7 和 2 可用于交换,选择两者中的较大者7放在树的顶部, 7 交换和 1 ,树变成了:
       71  25
  • 继续堆化直到该节点没有任何子节点或者它比两个子节点都要大为止。对于这个堆,只需要再有一次交换就恢复了堆属性:
       75  2
  • 删除操作的 C 语言代码示例
int deleteHeap(int heap[], int *size) {if (*size == 0) {return -1;}int value = heap[0];heap[0] = heap[*size - 1];(*size)--;int index = 0;while (1) {int leftChildIndex = 2 * index + 1;int rightChildIndex = 2 * index + 2;int largestIndex = index;if (leftChildIndex < *size && heap[leftChildIndex] > heap[largestIndex]) {largestIndex = leftChildIndex;}if (rightChildIndex < *size && heap[rightChildIndex] > heap[largestIndex]) {largestIndex = rightChildIndex;}if (largestIndex!= index) {int temp = heap[index];heap[index] = heap[largestIndex];heap[largestIndex] = temp;index = largestIndex;} else {break;}}return value;
}

(三)最大堆

 1.构造最大堆

  • 基本思想:首先将每个叶子节点视为一个堆,再将每个叶子节点与其父节点一起构造成一个包含更多节点的堆。所以,在构造堆的时候,首先需要找到最后一个节点的父节点,从这个节点开始构造最大堆;直到该节点前面所有分支节点都处理完毕,这样最大堆就构造完毕了。
  • 假设树的节点个数为n,以1为下标开始编号,直到结束。对于节点 i ,其父节点为i/2;左孩子节点为i*2,右孩子节点为i*2+1。最后一个节点的下标为n,其父节点的下标为n/2
  • 例如,原始数据为a\left [ \right ]=\left \{ 4,1,3,2,16,9,10,14,8,7 \right \},采用顺序存储方式,对应的完全二叉树构建最大堆的过程如下:
    • 首先,最后一个节点为7,其父节点为16,从16这个节点开始构造最大堆。
    • 构造完毕之后,转移到下一个父节点2,直到所有父节点都构造完毕。
    • 具体过程如下图所示(括号内为每次调整后的结果)
       41 3 (3 1)2 16 9 10 (14 16 9 10)14 8 7 (2 8 7)(a)       (b)41 10 (10 1)14 16 9 3 (14 16 9 3)2 8 7 (2 8 7)(c)       (d)1614 108 7 9 32 4 1(e) 堆的初始化步骤
  • 代码实现
struct MaxHeap {Etype *heap; //数据元素存放的空间,下标从1开始存数数据,下标为0的作为工作空间,存储临时数据int HeapSize; //数据元素的个数int MaxSize; //存放数据元素空间的大小
};
MaxHeap H;void MaxHeapInit(MaxHeap &H) {for (int i = H.HeapSize / 2; i >= 1; i--) {H.heap[0] = H.heap[i];int son = i * 2;while (son <= H.HeapSize) {if (son < H.HeapSize && H.heap[son] < H.heap[son + 1])son++;if (H.heap[0] >= H.heap[son])break;else {H.heap[son / 2] = H.heap[son];son *= 2;}}H.heap[son / 2] = H.heap[0];}
}

2.最大堆插入节点

  • 思想:先在堆的最后添加一个节点,然后沿着堆树上升。跟最大堆的初始化过程大致相同。

代码实现: 

void MaxHeapInsert(MaxHeap &H, EType &x) {if (H.HeapSize == H.MaxSize)return false;int i = ++H.HeapSize;while (i!= 1 && x > H.heap[i / 2]) {H.heap[i] = H.heap[i / 2];i = i / 2;}H.heap[i] = x;return true;
}

3.最大堆堆顶节点删除

  • 思想:将堆树的最后的节点提到根结点,然后删除最大值,然后再把新的根节点放到合适的位置。

代码实现: 

void MaxHeapDelete(MaxHeap &H, EType &x) {if (H.HeapSize == 0)return false;x = H.heap[1];H.heap[0] = H.heap[H.HeapSize--];int i = 1, son = i * 2;while (son <= H.HeapSize) {if (son <= H.HeapSize && H.heap[0] < H.heap[son + 1])son++;if (H.heap[0] >= H.heap[son])break;H.heap[i] = H.heap[son];i = son;son = son * 2;}H.heap[i] = H.heap[0];return true;
}

(四)最小堆

整体操作和最大堆类似,主要区别在于比较大小的规则相反。在最小堆中,每个节点的值都不大于其子节点的值。插入和删除操作时,调整堆的方向也与最大堆相反,以保持最小堆的性质。

四、堆的应用

(一)堆排序

1.原理

堆排序利用了堆(通常是最大堆或最小堆)的特性来实现排序。对于最大堆,每次可以取出堆顶元素(即当前堆中的最大值),然后将堆的最后一个元素放到堆顶,再进行调整使堆保持最大堆性质。重复这个过程,就可以得到一个有序的序列。

2.示例

例如,对数组进行堆排序。首先根据该数组元素构建一个完全二叉树,具体过程如下(从左到右,从上到下按顺序一步一步的详细过程):

  • 初始完全二叉树:
       167320178
  • 构建最大堆过程:
       16 16 207 8 20 8 16 820 17 3 7
       201787163
       317 816 20173 87 16 2017 3 1616 8 16 8 3 87 3 7 20 716 3 87 8 7 8 7 33 20 163 7 37 8 3 9 81 17 20 16 16 
  • 然后进行堆排序,每次取出堆顶元素(最大值),与数组末尾元素交换,再对剩余元素进行调整,重复这个过程,最终得到有序数组。

  • 相关参考文章:我的十大经典排序算法 (C++、Java 实现) 动图演示及代码解析 - 冒泡排序、选择排序、插入排序、快速排序、堆排序、希尔排序、归并排序、计数排序、桶排序、基数排序

        有各个经典排序算法的动图介绍和源码,可以参考。 

(二)优先队列

堆可以用于实现优先队列。在优先队列中,元素按照优先级进行排序。最大堆可以用于实现最大优先队列,每次取出的元素是优先级最高(值最大)的元素;最小堆可以用于实现最小优先队列,每次取出的元素是优先级最高(值最小)的元素。插入和删除元素时,堆的操作可以保证优先队列的性质始终满足。

五、总结

是一种非常重要的数据结构,具有独特的性质和高效的操作。它在堆排序、优先队列等算法和应用中有着广泛的应用。通过对堆的深入理解和掌握,我们可以更好地设计和实现高效的算法,解决各种实际问题。在未来的计算机科学领域,堆的应用可能会随着技术的发展而更加广泛,例如在大数据处理、实时系统等方面发挥更大的作用。同时,对于堆的研究和优化也将不断深入,以提高其性能和适用性。希望大家在学习堆的过程中,能充分体会到它的魅力和价值。

如果在阅读过程中有任何疑问或建议,欢迎随时交流。


💝💝💝感谢你看到最后,点个赞再走吧!💝💝💝 

以下是一个投票,欢迎你参与: 

相关文章:

【数据结构】堆(Heap)详解

在深入了解堆这一重要的数据结构之前&#xff0c;不妨先回顾一下我之前的作品 ——“二叉树详解”。 上篇文章&#x1f449;剖析二叉树&#xff08;Binary Tree&#xff09; 二叉树作为一种基础的数据结构&#xff0c;为我们理解堆以及其他更复杂的数据结构奠定了坚实的基础。它…...

《Linux从小白到高手》理论篇(四):Linux用户和组相关的命令

List item 本篇介绍Linux用户和组相关的命令&#xff0c;看完本文&#xff0c;有关Linux用户和组相关的常用命令你就掌握了99%了。Linux用户和组相关的命令可以分为以下六类&#xff1a; 一.用户和用户组相关查询操作命令&#xff1a; Id id命令用于显示用户的身份标识。常见…...

OpenGL ES 之EGL(6)

OpenGL ES 之EGL(6) 简述 EGL是OpenGL ES的封装&#xff0c;目的是跨设备跨平台&#xff0c;隔离不同平台对窗口不同的实现。上一节我们基本没有使用到EGL&#xff0c;因为GLSurfaceView帮助我们处理了相关的逻辑&#xff0c;我们这一节来看一下EGL的一些概念以及接口的使用。…...

kotlin 委托

一、类委托 interface DB{fun insert() } class SqliteDB : DB {override fun insert() {println(" SqliteDB insert")} }class MySql : DB{override fun insert() {println(" MySql insert")} }class OracleDB : DB{override fun insert() {println(&quo…...

Stream流的中间方法

一.Stream流的中间方法 注意1&#xff1a;中间方法&#xff0c;返回新的Stream流&#xff0c;原来的Stream流只能使用一次&#xff0c;建议使用链式编程 注意2&#xff1a;修改Stream流中的数据&#xff0c;不会影响原来集合或者数组中的数据 二.filter filter的主要用法是…...

【车载开发系列】ParaSoft单元测试环境配置(四)

【车载开发系列】ParaSoft单元测试环境配置&#xff08;四&#xff09; 【车载开发系列】ParaSoft单元测试环境配置&#xff08;四&#xff09; 【车载开发系列】ParaSoft单元测试环境配置&#xff08;四&#xff09;一. 如何设置过滤二. 如何设置静态扫描的规则三. 如何设置单…...

IDEA 设置自动定位文件

一、场景分析 IDEA 在使用的过程中&#xff0c;发现有时候&#xff0c;打开一个类&#xff0c;它并不能自动帮我们在左侧 Project 树中定位出文件&#xff0c;需要自己手动点击 瞄准 图标。很不方便。 二、解决方法 1、点击 瞄准 图标旁边的 竖三点 2、将 Alwasy Select Opene…...

Nature Machine Intelligence 基于强化学习的扑翼无人机机翼应变飞行控制

尽管无人机技术发展迅速&#xff0c;但复制生物飞行的动态控制和风力感应能力&#xff0c;仍然遥不可及。生物学研究表明&#xff0c;昆虫翅膀上有机械感受器&#xff0c;即钟形感受器campaniform sensilla&#xff0c;探测飞行敏捷性至关重要的复杂气动载荷。 近日&#xff0…...

[Web安全 网络安全]-XXE 外部实体注入攻击XML

文章目录&#xff1a; 一&#xff1a;前言 1.定义 1.1 XXE 1.2 XML可扩展标记语言 2.DDT文档类型定义 2.1 分类 2.2 元素element DTD元素 DTD属性 2.3 实体entity DTD实体类别 DTD实体声明引用 声明&#xff1a;内部 外部 参数实体 公共实体 引用&#xff1a;…...

8--苍穹外卖-SpringBoot项目中套餐管理 详解(二)

目录 删除套餐 需求分析和设计 代码开发 根据id查询套餐 mapper层 Service层 ServiceImpl层 Mapper层 批量删除套餐 mapper层 Service层 ServiceImpl层 Mapper层 SetmealMapper.xml 修改套餐 需求分析和设计 代码开发 起售停售套餐 需求分析和设计 代码开发…...

测试面试题:pytest断言时,数据是符点类型,如何断言?

在使用 Pytest 进行断言时&#xff0c;如果数据是浮点类型&#xff0c;可以使用以下方法进行断言&#xff1a; 一、使用pytest.approx pytest.approx可以用来比较两个浮点数是否近似相等。例如&#xff1a; import pytestdef test_float_assertion():result 3.14159expecte…...

Python与MongoDB交互

一、基本概念 MongoDB: 一个面向文档的数据库系统&#xff0c;使用BSON&#xff08;Binary JSON&#xff09;作为存储格式。集合&#xff08;Collection&#xff09;: 类似于关系型数据库中的表&#xff0c;是文档的集合。文档&#xff08;Document&#xff09;: MongoDB中的基…...

安卓AI虚拟女友项目开发的Android开发环境搭建

第五章&#xff1a;Android开发环境搭建与基础入门 5-1 项目讲解思路说明 本文是安卓AI数字虚拟人项目实战的第五章&#xff0c;开发安卓AI安卓版数字虚拟人的Android基础部分。 在本章中&#xff0c;我们将详细介绍如何搭建Android开发环境&#xff0c;包括Android Studio的…...

基于SpringBoot+Vue+MySQL的智能垃圾分类系统

系统展示 用户前台界面 管理员后台界面 系统背景 随着城市化进程的加速&#xff0c;垃圾问题日益凸显&#xff0c;不仅对环境造成污染&#xff0c;也给城市管理带来了巨大挑战。传统的垃圾分类方式不仅费时费力&#xff0c;而且手工操作容易出现错误&#xff0c;导致垃圾分类效…...

你的个人文件管理助手:AI驱动的本地文件整理工具

&#x1f310; 引言 在数字化时代&#xff0c;我们经常面临文件管理的挑战。电脑中的文件杂乱无章&#xff0c;寻找特定文件变得既费时又费力。幸运的是&#xff0c;现在有了一款名为本地文件整理器的神器&#xff0c;它利用AI技术帮助你快速、智能地整理文件&#xff0c;同时…...

【PyTorch】环境配置

框架介绍 Pytorch简介 2017年1月&#xff0c;FAIR&#xff08;Facebook AI Research&#xff09;发布了PyTorch。PyTorch是在Torch基础上用python语言重新打造的一款深度学习框架。Torch是采用Lua语言作为接口的机器学习框架&#xff0c;但因为Lua语言较为小众&#xff0c;导…...

枫叶MTS格式转换器- 强大、操作简单的MTS、M2TS视频转换工具供大家学习研究参考

一款功能强大、操作简单的MTS、M2TS视频转换工具,欢迎下载使用。 使用本MTS格式转换器可以帮助您将索尼和松下等摄像机录制的MTS、M2TS格式高清视频转换为其他流行的视频格式,如MP4、3GP、AVI、MPEG、WMV、ASF、MOV、RM、VCD、SVCD、DVD、MKV、FLV、SWF、MPG、MP3、WAV、WMA…...

Vscode把全部‘def‘都收起来的快捷键

在 VSCode 中&#xff0c;你可以使用以下快捷键来收起所有函数定义 (def)&#xff1a; Windows/Linux: Ctrl K, Ctrl 0macOS: Cmd K, Cmd 0 这个快捷键组合会折叠当前文件中所有的代码块&#xff08;包括所有函数和类定义&#xff09;。你可以通过相同的快捷键再次展开这…...

Web和UE5像素流送、通信教程

一、web端配置 首先打开Github地址&#xff1a;https://github.com/EpicGamesExt/PixelStreamingInfrastructure 找到自己虚幻引擎对应版本的项目并下载下来&#xff0c;我这里用的是5.3。 打开项目找到PixelStreamingInfrastructure-master > Frontend > implementat…...

【YOLO目标检测电梯间电动车与人数据集】共4321张、已标注txt格式、有训练好的yolov5的模型

目录 说明图片示例 说明 数据集格式&#xff1a;YOLO格式 图片数量&#xff1a;4321 标注数量(txt文件个数)&#xff1a;4321 标注类别数&#xff1a;2 标注类别名称&#xff1a;person、electricBicycle 数据集下载&#xff1a;电梯间电动车与人数据集 图片示例 数据…...

synchronized 学习

学习源&#xff1a; https://www.bilibili.com/video/BV1aJ411V763?spm_id_from333.788.videopod.episodes&vd_source32e1c41a9370911ab06d12fbc36c4ebc 1.应用场景 不超卖&#xff0c;也要考虑性能问题&#xff08;场景&#xff09; 2.常见面试问题&#xff1a; sync出…...

电脑插入多块移动硬盘后经常出现卡顿和蓝屏

当电脑在插入多块移动硬盘后频繁出现卡顿和蓝屏问题时&#xff0c;可能涉及硬件资源冲突、驱动兼容性、供电不足或系统设置等多方面原因。以下是逐步排查和解决方案&#xff1a; 1. 检查电源供电问题 问题原因&#xff1a;多块移动硬盘同时运行可能导致USB接口供电不足&#x…...

SpringCloudGateway 自定义局部过滤器

场景&#xff1a; 将所有请求转化为同一路径请求&#xff08;方便穿网配置&#xff09;在请求头内标识原来路径&#xff0c;然后在将请求分发给不同服务 AllToOneGatewayFilterFactory import lombok.Getter; import lombok.Setter; import lombok.extern.slf4j.Slf4j; impor…...

多模态大语言模型arxiv论文略读(108)

CROME: Cross-Modal Adapters for Efficient Multimodal LLM ➡️ 论文标题&#xff1a;CROME: Cross-Modal Adapters for Efficient Multimodal LLM ➡️ 论文作者&#xff1a;Sayna Ebrahimi, Sercan O. Arik, Tejas Nama, Tomas Pfister ➡️ 研究机构: Google Cloud AI Re…...

Mysql中select查询语句的执行过程

目录 1、介绍 1.1、组件介绍 1.2、Sql执行顺序 2、执行流程 2.1. 连接与认证 2.2. 查询缓存 2.3. 语法解析&#xff08;Parser&#xff09; 2.4、执行sql 1. 预处理&#xff08;Preprocessor&#xff09; 2. 查询优化器&#xff08;Optimizer&#xff09; 3. 执行器…...

苹果AI眼镜:从“工具”到“社交姿态”的范式革命——重新定义AI交互入口的未来机会

在2025年的AI硬件浪潮中,苹果AI眼镜(Apple Glasses)正在引发一场关于“人机交互形态”的深度思考。它并非简单地替代AirPods或Apple Watch,而是开辟了一个全新的、日常可接受的AI入口。其核心价值不在于功能的堆叠,而在于如何通过形态设计打破社交壁垒,成为用户“全天佩戴…...

MySQL 主从同步异常处理

阅读原文&#xff1a;https://www.xiaozaoshu.top/articles/mysql-m-s-update-pk MySQL 做双主&#xff0c;遇到的这个错误&#xff1a; Could not execute Update_rows event on table ... Error_code: 1032是 MySQL 主从复制时的经典错误之一&#xff0c;通常表示&#xff…...

【SpringBoot自动化部署】

SpringBoot自动化部署方法 使用Jenkins进行持续集成与部署 Jenkins是最常用的自动化部署工具之一&#xff0c;能够实现代码拉取、构建、测试和部署的全流程自动化。 配置Jenkins任务时&#xff0c;需要添加Git仓库地址和凭证&#xff0c;设置构建触发器&#xff08;如GitHub…...

云安全与网络安全:核心区别与协同作用解析

在数字化转型的浪潮中&#xff0c;云安全与网络安全作为信息安全的两大支柱&#xff0c;常被混淆但本质不同。本文将从概念、责任分工、技术手段、威胁类型等维度深入解析两者的差异&#xff0c;并探讨它们的协同作用。 一、核心区别 定义与范围 网络安全&#xff1a;聚焦于保…...

2025-05-08-deepseek本地化部署

title: 2025-05-08-deepseek 本地化部署 tags: 深度学习 程序开发 2025-05-08-deepseek 本地化部署 参考博客 本地部署 DeepSeek&#xff1a;小白也能轻松搞定&#xff01; 如何给本地部署的 DeepSeek 投喂数据&#xff0c;让他更懂你 [实验目的]&#xff1a;理解系统架构与原…...