当前位置: 首页 > 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;电梯间电动车与人数据集 图片示例 数据…...

LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器的上位机配置操作说明

LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器专为工业环境精心打造&#xff0c;完美适配AGV和无人叉车。同时&#xff0c;集成以太网与语音合成技术&#xff0c;为各类高级系统&#xff08;如MES、调度系统、库位管理、立库等&#xff09;提供高效便捷的语音交互体验。 L…...

从零实现富文本编辑器#5-编辑器选区模型的状态结构表达

先前我们总结了浏览器选区模型的交互策略&#xff0c;并且实现了基本的选区操作&#xff0c;还调研了自绘选区的实现。那么相对的&#xff0c;我们还需要设计编辑器的选区表达&#xff0c;也可以称为模型选区。编辑器中应用变更时的操作范围&#xff0c;就是以模型选区为基准来…...

(二)TensorRT-LLM | 模型导出(v0.20.0rc3)

0. 概述 上一节 对安装和使用有个基本介绍。根据这个 issue 的描述&#xff0c;后续 TensorRT-LLM 团队可能更专注于更新和维护 pytorch backend。但 tensorrt backend 作为先前一直开发的工作&#xff0c;其中包含了大量可以学习的地方。本文主要看看它导出模型的部分&#x…...

vue3 字体颜色设置的多种方式

在Vue 3中设置字体颜色可以通过多种方式实现&#xff0c;这取决于你是想在组件内部直接设置&#xff0c;还是在CSS/SCSS/LESS等样式文件中定义。以下是几种常见的方法&#xff1a; 1. 内联样式 你可以直接在模板中使用style绑定来设置字体颜色。 <template><div :s…...

spring:实例工厂方法获取bean

spring处理使用静态工厂方法获取bean实例&#xff0c;也可以通过实例工厂方法获取bean实例。 实例工厂方法步骤如下&#xff1a; 定义实例工厂类&#xff08;Java代码&#xff09;&#xff0c;定义实例工厂&#xff08;xml&#xff09;&#xff0c;定义调用实例工厂&#xff…...

Spring AI 入门:Java 开发者的生成式 AI 实践之路

一、Spring AI 简介 在人工智能技术快速迭代的今天&#xff0c;Spring AI 作为 Spring 生态系统的新生力量&#xff0c;正在成为 Java 开发者拥抱生成式 AI 的最佳选择。该框架通过模块化设计实现了与主流 AI 服务&#xff08;如 OpenAI、Anthropic&#xff09;的无缝对接&…...

Swagger和OpenApi的前世今生

Swagger与OpenAPI的关系演进是API标准化进程中的重要篇章&#xff0c;二者共同塑造了现代RESTful API的开发范式。 本期就扒一扒其技术演进的关键节点与核心逻辑&#xff1a; &#x1f504; 一、起源与初创期&#xff1a;Swagger的诞生&#xff08;2010-2014&#xff09; 核心…...

HashMap中的put方法执行流程(流程图)

1 put操作整体流程 HashMap 的 put 操作是其最核心的功能之一。在 JDK 1.8 及以后版本中&#xff0c;其主要逻辑封装在 putVal 这个内部方法中。整个过程大致如下&#xff1a; 初始判断与哈希计算&#xff1a; 首先&#xff0c;putVal 方法会检查当前的 table&#xff08;也就…...

管理学院权限管理系统开发总结

文章目录 &#x1f393; 管理学院权限管理系统开发总结 - 现代化Web应用实践之路&#x1f4dd; 项目概述&#x1f3d7;️ 技术架构设计后端技术栈前端技术栈 &#x1f4a1; 核心功能特性1. 用户管理模块2. 权限管理系统3. 统计报表功能4. 用户体验优化 &#x1f5c4;️ 数据库设…...

人工智能(大型语言模型 LLMs)对不同学科的影响以及由此产生的新学习方式

今天是关于AI如何在教学中增强学生的学习体验&#xff0c;我把重要信息标红了。人文学科的价值被低估了 ⬇️ 转型与必要性 人工智能正在深刻地改变教育&#xff0c;这并非炒作&#xff0c;而是已经发生的巨大变革。教育机构和教育者不能忽视它&#xff0c;试图简单地禁止学生使…...