数据结构 - 堆
今天我们将学习新的数据结构-堆。
01定义
堆是一种特殊的二叉树,并且满足以下两个特性:
(1)堆是一棵完全二叉树;
(2)堆中任意一个节点元素值都小于等于(或大于等于)左右子树中所有节点元素值;
小根堆,根节点元素永远是最小值,即堆中每个节点元素值都小于等于左右子树中所有节点元素值;
大根堆,根节点元素永远是最大值,即堆中每个节点元素值都大于等于左右子树中所有节点元素值;
根据堆的定义我们不难发现,堆特别适合用来求集合最值,以及求最值引申的问题比如:排序、优先队列、动态排名等等
02结构
我们指定堆是一种特殊完全二叉树,因此堆的逻辑结构是树。
我们指定树的存储结构有两种,顺序存储(数组)和链式存储(链表),那么堆的存储结构是什么呢?
既然堆是完全二叉树,那么树的存储结构当然也适用于堆。但是堆一般都选用顺序存储(数组)实现。其原因有三:
(1)位置计算简单:数组实现堆可以使用完全二叉树特性用简单的数学公式即可表示父子节点的索引关系,从而避免了链表实现使用额外的指针,即减少内存开销和实现复杂度;
(2)性能好:数组的连续内存特性使得其有高效的访问速度,而链表因为节点不一定连续访问速度相对较差;
(3)操作简单:数组实现在逻辑实现上更加简单高效,通过交换数组中的元素即可快速实现堆的性质,链表实现在插入和删除操作中需要遍历链表效率远不如数组实现;
总结一句话数组简单、内存连续、性能更好,所以一般选用数组实现堆,当然不一般的情况也可以使用链表实现。
03实现(最小堆)
下面我们就用数组来实现一个最小堆结构,最大堆只是比较大小不同这里就不做过多赘述。
1、初始化 Init
首先我们需要定义两个私有变量,一个变量用来存放堆元素的数组,一个变量用来存放数组尾元素索引,主要用来跟踪当前堆元素个数情况。
而初始化方法就是初始化两个变量,创建指定容量数组,以及标记数组尾元素索引为-1,表示堆中还没有元素。
//存放堆元素
private int[] _array;
//数组尾元素索引
private int _tailIndex;
//初始化堆为指定容量
public MyselfMinHeap Init(int capacity)
{//初始化指定长度数组用于存放堆元素_array = new int[capacity];//初始化数组尾元素索引为-1,表示空数组_tailIndex = -1;//返回堆return this;
}
2、获取堆容量 Capacity
堆容量指的是数组的固定空间,即数组最多能容纳多少个元素,直接返回数组长度即可。
//堆容量
public int Capacity
{get{return _array.Length;}
}
3、获取堆元素数量 Count
堆元素数量指当前堆中一共有多少个元素,我们可以通过私有字段数组尾元素索引值加1获得。
//堆元素数量
public int Count
{get{//数组尾元素索引加1即为堆元素数量return _tailIndex + 1;}
}
4、获取堆顶元素 Top
堆顶元素指树节点中的根节点,也就是数组中的第一个元素。同时要注意判断数组是否为空,为空报异常。
//获取堆顶元素,即最小元素
public int Top
{get{if (IsEmpty()){//空堆,不可以进行获取最小堆元素操作throw new InvalidOperationException("空堆");}return _array[0];}
}
5、是否为空堆 IsEmpty
空堆即堆中还没有任何元素,当数组尾元素索引为-1时表示空堆。
//检查堆是否为空
public bool IsEmpty()
{return _tailIndex == -1;
}
6、是否为满堆 IsFull
满堆即堆空间已满不能再添加新元素,当数组尾元素索引等于数组容量减1时表示满堆。
//检查堆是否为满堆
public bool IsFull()
{//数组末尾元素索引等于数组容量减1表示满堆return _tailIndex == _array.Length - 1;
}
7、入堆 Push
入堆即向堆中新添加一个元素。
而入堆也涉及到一个问题,就是如何保存每次添加完新元素后,还保持着堆的特性。很显然我们也没办法做到直接把新元素直接插入到其正确的位置上,因此我们可以梳理新添加一个元素的流程,可能大致有以下几个步骤:
(1)首先把新元素添加到堆的末尾;
(2)然后调整堆使其满足堆的性质;
(3)更新堆尾元素索引;
而难点显然就在第二步,如何调整数组使其满足堆的性质?
我们先直接模拟把7654按顺序推入堆中,看看如何实现。
(1)首先7入堆,此时只有一个元素,无需做任何操作,而7就作为根节点;
(2)然后6入堆,此时已有两个元素,因此需要保持堆的两个特性:根节点永远是最小元素和堆是完全二叉树。由完全二叉树特性可得,根节点左子节点索引为20+1=1,而右子节点索引为20+2=2,而此时6的索引为1,所以6为左子节点;又因6比7小,所以根节点变为6, 7变为根节点的左子节点;
(3)然后5入堆,此时已有3个元素,所以5的索引为2,而根节点右子节点索引为2*0+2=2,所以5添加为根节点的右子节点。5比6小,所以5和6交互位置;
(4)然后4入堆,此时已有4个元素,所以4的索引为3,而7节点左子节点索引为2*1+1=3,所以4添加为7节点的左子节点。4比7小,所以4和7交互位置; 再次比较4和5,4比5小,所以4和5交互位置;
相信到这里大家已经看出一点规律了,调整整个数组元素使其满足堆的性质的整个过程大致分为以下几个步骤:
(1)从新元素开始向上比较其与父节点元素的值大小;
(2)如果新元素值小于其父节点元素值,则交互两者位置,否则结束调整;
(3)重复以上步骤直至处理到根节点。
代码实现如下:
//入堆 向堆中添加一个元素
public void Push(int data)
{if (IsFull()){//满堆,不可以进行入添加元素操作throw new InvalidOperationException("满堆");}//新元素索引var index = _tailIndex + 1;//在数组末尾添加新元素_array[index] = data;//向上调整堆,以保持堆的性质SiftUp(index);//尾元素索引向后前进1位_tailIndex++;
}
//向上调整堆,以保持堆的性质
private void SiftUp(int index)
{//一直调整到堆顶为止while (index > 0){//计算父节点元素索引var parentIndex = (index - 1) / 2;//如果当前元素大于等于其父节点元素,则结束调整if (_array[index] >= _array[parentIndex]){break;}//否则,交换两个元素(_array[parentIndex], _array[index]) = (_array[index], _array[parentIndex]);//更新当前索引为父节点元素索引继续调整index = parentIndex;}
}
8、出堆 Pop
出堆即删除并返回堆顶元素(堆中最小元素)。
而出堆同样也涉及到和入堆同样的问题,就是如何保存每次删除元素后,还保持着堆的特性。
显然删除和添加元素情况还不一样。添加新元素后还是一棵完全二叉树,只不过可能没有满足堆的性质,所以需要调整。而删除就不一样了,想象一下当我们把堆顶元素删除后,回怎样?根节点空了,此时还能较完全二叉树吗?显然不能。
如何处理呢?直观的想法就是根节点空了就用其子节点补充上呗,就这样从上到下一直填补,直至最后的空落在了叶子节点那一层,如果这个空位落到了叶子节点左侧,而右侧还有值,此时就表明堆不满足完全二叉树这一特性,因此还需要把这个空位移到堆的末尾,想象都头大。这显然不是一个好办法。
既然我们最后要把根节点删除后的这个空位移到堆的末尾,何不直接把这个空位和堆的尾元素直接调换个位置呢,然后再参考出堆中调整整个数组使其满足堆的性质。
我们梳理整个流程,可能大致有以下几个步骤:
(1)首先获取堆顶元素并暂存;
(2)将堆尾元素赋值给堆顶元素,并将堆尾元素置空;
(3)然后调整堆使其满足堆性质;
(4)更新数组尾元素索引,并返回暂存的堆顶元素;
而难点同样在第二步,如何调整数组使其满足堆的性质?入堆是新元素在堆尾所以是从下往上进行调整,而出堆是堆顶元素需要调整是否可以从上往下进行调整呢?
我们把87654按顺序推入堆中后把4推出堆中,看看如何实现。
首先删除根节点4,并把堆尾元素8放到根节点;
为了保持堆特性,找出根节点及其子节点中最小元素并与当前根节点8交互位置,因为8>6>5,所以5与8交互位置;
然后8节点继续与其子节点进行比较,因为8>7,所以7与8交互位置。
这一出堆过程我们可以总结为以下步骤:
(1)从当前节点开始,找到其子节点元素值;
(2)比较当前节点元素值与其子节点元素值大小,如果当前节点值最小则结束调整,否则取值最小的与其交互;
(3)重复以上步骤直至处理到叶子节点。
代码实现如下:
//出堆 删除并返回堆中最小元素
public int Pop()
{if (IsEmpty()){//空堆,不可以进行删除并返回堆中最小元素操作throw new InvalidOperationException("空堆");}//取出数组第一个元素即最小元素var min = _array[0];//将数组末尾元素赋值给第一个元素_array[0] = _array[_tailIndex];//将数组末尾元素设为默认值_array[_tailIndex] = 0;//将数组末尾元素索引向前移动1位_tailIndex--;//向下调整堆,以保持堆的性质SiftDown(0);//返回最小元素return min;
}
//向下调整堆,以保持堆的性质
private void SiftDown(int index)
{while (index <= _tailIndex){//定义较小值索引变量,用于存放比较当前元素及其左右子节点元素中最小元素var minIndex = index;//计算右子节点索引var rightChildIndex = 2 * index + 2;//如果存在右子节点,则比较其与当前元素,保留值较小的索引if (rightChildIndex <= _tailIndex && _array[rightChildIndex] < _array[minIndex]){minIndex = rightChildIndex;}//计算左子节点索引var leftChildIndex = 2 * index + 1;//如果存在左子节点,则比较其与较小值元素,保留值较小的索引if (leftChildIndex <= _tailIndex && _array[leftChildIndex] < _array[minIndex]){minIndex = leftChildIndex;}//如果当前元素就是最小的,则停止调整if (minIndex == index){break;}//否则,交换当前元素和较小元素(_array[minIndex], _array[index]) = (_array[index], _array[minIndex]);//更新索引为较小值索引,继续调整index = minIndex;}
}
9、堆化 Heapify
堆化即把一个无序数组堆化成小根堆。
可以通过调用出堆是用到的调整方法来完成。大家可以思考一下为什么不是堆尾元素开始调整?为什么是从下往上调用向下调整方法调整?
//堆化,即把一个无序数组堆化成小根堆
public void Heapify(int[] array)
{if (array == null || _array.Length < array.Length){throw new InvalidOperationException("无效数组");}//将数组复制到堆中Array.Copy(array, _array, array.Length);//更新尾元素索引_tailIndex = array.Length - 1;//从最后一个非叶子节点开始向下调整堆for (int i = (array.Length / 2) - 1; i >= 0; i--){SiftDown(i);}
}
注:测试方法代码以及示例源码都已经上传至代码库,有兴趣的可以看看。https://gitee.com/hugogoos/Planner
相关文章:
数据结构 - 堆
今天我们将学习新的数据结构-堆。 01定义 堆是一种特殊的二叉树,并且满足以下两个特性: (1)堆是一棵完全二叉树; (2)堆中任意一个节点元素值都小于等于(或大于等于)左…...
html----图片按钮,商品展示
源码 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>图标</title><style>.box{width:…...
YOLOv11改进策略【卷积层】| ECCV-2024 小波卷积WTConv 增大感受野,降低参数量计算量,独家创新助力涨点
一、本文介绍 本文记录的是利用小波卷积WTConv模块优化YOLOv11的目标检测网络模型。WTConv的目的是在不出现过参数化的情况下有效地增加卷积的感受野,从而解决了CNN在感受野扩展中的参数膨胀问题。本文将其加入到深度可分离卷积中,有效降低模型参数量和计算量,并二次创新C3…...
redis高级篇之redis源码分析List类型quicklist底层演变 答疑159节
(1)ziplist压缩配置:list-compress-depth 0 表示一个quicklist两端不被压缩的节点个数。这里的节点是指quicklist双向链表的节点,而不是指ziplist里面的数据项个数参数list-compress-depth的取值含义如下: 0:是个特殊值,表示都不压缩。这是Redis的默认值…...
Elasticsearch 与 Lucene 的区别和联系
Elasticsearch 与 Lucene 的区别和联系 Elasticsearch 与 Lucene 的区别和联系一、知识背景Elasticsearch 简介Lucene 简介 二、Elasticsearch 和 Lucene 的区别适用场景性能优势和劣势架构设计的异同点 三、Elasticsearch和Lucene的联系四、Elasticsearch和Lucene的应用案例及…...
OpenCV视觉分析之运动分析(5)背景减除类BackgroundSubtractorMOG2的使用
操作系统:ubuntu22.04 OpenCV版本:OpenCV4.9 IDE:Visual Studio Code 编程语言:C11 算法描述 基于高斯混合模型的背景/前景分割算法。 该类实现了在文献[320]和[319]中描述的高斯混合模型背景减除。 cv::BackgroundSubtractorMOG2 类是 O…...
【SAP Hana】X-DOC:数据仓库ETL如何抽取SAP中的CDS视图数据
【SAP Hana】X-DOC:数据仓库ETL如何抽取SAP中的CDS视图数据 1、无参CDS对应数据库视图2、有参CDS对应数据库表函数3、封装有参CDS为无参CDS,从而对应数据库视图 1、无参CDS对应数据库视图 select * from ZFCML_REP_V where mandt 300;2、有参CDS对应数…...
WPF的UpdateSourceTrigger属性
在WPF中,UpdateSourceTrigger属性用于控制数据绑定中何时将绑定目标(通常是UI元素)的值更新回绑定源(通常是数据对象)。这个属性有以下几个值: Default:这是默认值,对于不同的绑定目…...
2024-09-25 环境变量,进程地址空间
一、认识常见的环境变量 1. echo $HOME 输出当前用户对应的家目录 当用户登录系统时,流程如下: (1)用户登录系统后,系统启动Shell程序。 (2)启动bash shell,准备接收用户指令。 &a…...
中国移动机器人将投入养老场景;华为与APUS共筑AI医疗多场景应用
AgeTech News 一周行业大事件 华为与APUS合作,共筑AI医疗多场景应用 中国移动展出人形机器人,预计投入养老等场景 作为科技与奥富能签约,共拓智能适老化改造领域 天与养老与香港科技园,共探智慧养老新模式 中山大学合作中国…...
青少年编程能力等级测评CPA C++ 四级试卷(1)
青少年编程能力等级测评CPA C 四级试卷(1) 一、单项选择题(共15题,每题3分,共45分) CP4_1_1.在面向对象程序设计中,与数据构成一个相互依存的整体的是( )。 A. 对数据…...
树上任意两点的距离
题目描述 给出 n 个点的一棵树,多次询问两点之间的最短距离。 注意:边是双向的。 输入描述 第一行为两个整数 n 和 m。n 表示点数,m 表示询问次数; 下来 n−1 行,每行三个整数 x,y,k,表示点 x 和点 y 之间…...
【 thinkphp8 】00008 thinkphp8数据查询,常用table,name方法,进行数据查询汇总
前言:哈喽,大家好,今天给大家分享一篇文章!并提供具体代码帮助大家深入理解,彻底掌握!创作不易,如果能帮助到大家或者给大家一些灵感和启发,欢迎收藏关注哦 💕 目录 【 t…...
Git的命令合集
关于Git的一些命令合集,会慢慢更新! 20241024程序员节开始写的,记录一下~~ git查看log、查看详细提交记录 会显示之前的提交记录 , 排序由近及远 git log log按q退出 git回退到某个commit命令: 退到/进到指定commit的sha码&…...
博客搭建之路:hexo搜索引擎收录
文章目录 hexo搜索引擎收录以百度为例 hexo搜索引擎收录 hexo版本5.0.2 npm版本6.14.7 next版本7.8.0 写博客的目的肯定不是就只有自己能看到,想让更多的人看到就需要可以让搜索引擎来收录对应的文章。hexo支持生成站点地图sitemap 在hexo下的_config.yml中配置站点…...
创建Windows系统还原点
系统保护...
Linux等保测评需要用到的命令
三权设置 查看账户情况 cd /home/ ll 设置审计账户 useradd shenji passwd shenji 修改密码 passwd新密码 设置管理账户 useradd guanli passwd guanli compgen -u 查看用户 切换到root账户 su root 设置审计用户权限 vim /etc/sudoers shenji ALL (root) NOPASSWD:…...
PostgreSQL的学习心得和知识总结(一百五十六)|auto_explain — log execution plans of slow queries
目录结构 注:提前言明 本文借鉴了以下博主、书籍或网站的内容,其列表如下: 1、参考书籍:《PostgreSQL数据库内核分析》 2、参考书籍:《数据库事务处理的艺术:事务管理与并发控制》 3、PostgreSQL数据库仓库…...
数据结构模板代码合集(不完整)
P3368 【模板】树状数组 2 #include <bits/stdc.h> using namespace std; const int maxn 5e5 7;int n, m, s, t; int ans; int a[maxn]; struct node{int l, r;int num; }tr[maxn * 4];void build(int p, int l, int r){tr[p] {l, r, 0};if(l r){tr[p].num a[l];r…...
shell脚本语法详解
目录 shell语法基础 指定shell解析器 注释 运行 变量 定义变量 引用变量 清除变量值 从键盘获取值 输入单值 添加输入提示语 读取多值 编辑 定义只读变量 环境变量 设置环境变量与查看环境变量 特殊变量 三种引号的作用与区别 小括号与大括号 参数传递 位…...
2021亚洲机器学习会议:面向单阶段跨域检测的域自适应YOLO(ACML2021)
原文标题:Domain Adaptive YOLO for One-Stage Cross-Domain Detection 中文标题:面向单阶段跨域检测的域自适应YOLO 1、Abstract 域转移是目标检测器在实际应用中推广的主要挑战。两级检测器的域自适应新兴技术有助于解决这个问题。然而,两级…...
面试题:描述在前端开发中,如何利用数据结构来优化页面渲染性能,并给出一个具体的示例。
在前端开发中,优化页面渲染性能是提升用户体验的关键之一。合理地使用数据结构可以有效地减少DOM操作的次数、提高数据处理的效率,从而加快页面的渲染速度。以下是一些策略,并给出一个具体的示例。 1. 使用合适的数据结构 数组与对象&#…...
微积分复习笔记 Calculus Volume 1 - 3.2 he Derivative as a Function
3.2 The Derivative as a Function - Calculus Volume 1 | OpenStax...
html 轮播图效果
轮播效果: 1、鼠标没有移入到banner,自动轮播 2、鼠标移入:取消自动轮播、移除开始自动轮播 3、点击指示点开始轮播到对应位置 4、点击前一个后一个按钮,轮播到上一个下一个图片 注意 最后一个图片无缝滚动,就是先克隆第一个图片…...
Android Room(SQLite) too many SQL variables异常
SQLiteException 一、解决办法1. 修改数据库语句2. 分批执行 二、问题根源 转载请注明出处: https://blog.csdn.net/hx7013/article/details/143198862 在使用 Room 或其他基于 SQLite 的 ORM 框架时,批量操作如 IN 或 NOT IN 查询可能会触发 android.database.sqli…...
sentinel原理源码分析系列(八)-熔断
限流为了防止过度使用资源造成系统不稳,熔断是为了识别出”坏”资源,避免好的资源受牵连(雪崩效应),是保证系统稳定性的关键,也是资源有效使用的关键,sentinel熔断插槽名称Degrade(降级),本人觉得应该改为熔…...
安全见闻(4)——开阔眼界,不做井底之蛙
内容预览 ≧∀≦ゞ 安全见闻四:操作系统安全机制深度解析声明操作系统机制1. 注册表2. 防火墙3. 自启动与计划任务4. 事件日志5. 内核驱动与设备驱动6. 系统服务7. 进程与线程8. 系统编程 从操作系统机制看病毒设计1. 自启动:病毒如何在系统启动时运行&a…...
(二十二)、k8s 中的关键概念
文章目录 1、总体概览2、第一层:物理机、集群、Node、Pod 之间的关系2、第二层:命名空间 Namespace3、定义4、控制平面(Control Plane)5、特别的概念 Service6、Deployment 经过 之前几篇文章对 k8s 的实践,结合实践&…...
python基础综合案例(数据可视化-地图可视化)
1.基础地图使用 注意写名字的时候要写全名,比如上海市不能写出上海,不然看不到数据 鼠标点击即可看到数据 设置属性的时候不要忘记导包 # 演示地图可视化的基础使用 from pyecharts.charts import Map from pyecharts.options import VisualMapOpts # 准…...
基于SpringBoot足球场在线预约系统的设计与实现
💗博主介绍💗:✌在职Java研发工程师、专注于程序设计、源码分享、技术交流、专注于Java技术领域和毕业设计✌ 温馨提示:文末有 CSDN 平台官方提供的老师 Wechat / QQ 名片 :) Java精品实战案例《700套》 2025最新毕业设计选题推荐…...
广州专业网站制作公司/销售平台排名
例如,用户名test1改为test2,在plsql界面中不支持直接更改用户名,只能通过sql更改1、查询系统user$中的user#值select user#,name from user$ where name test1;2、根据user#值,更改用户名update user$ set nametest2 where user…...
wordpress网站如何播放视频/网络营销评价的名词解释
让我们来考虑这样一个问题,现在给定一个数据集,让你划分出决策边界,该怎么办呢?数据集中正样本为「叉」,负样本为「圈」,如下图:第一反应就是用一个高阶多项式去构造一个假设函数,当…...
做优品购类似网站/南昌seo报价
转载于:https://www.cnblogs.com/tyoutetu/p/11018888.html...
深圳宝安区好不好/苏州seo门户网
solaris下默认的ls后显示的目录或者文件等等是不带颜色的,如果要使其像linux下,那么需要如下步骤一、去www.sunfreeware.com下载coreutils-4.5.4-sol9-sparc-local.gz二、以下分十步完成1. 彩色输出是GUN版本ls命令的一个特征,所以你必须先得到这个版本&…...
网站建站ddp/金花关键词工具
前言:前面总结学习了图片的使用以及Lru算法,今天来学习一下比较优秀的图片缓存开源框架。技术本身就要不断的更迭,从最初的自己使用SoftReference实现自己的图片缓存,到后来做电商项目自己的实现方案不能满足项目的需求改用Afinal…...
网站建设 秦皇岛公司/qq关键词排名优化
2019独角兽企业重金招聘Python工程师标准>>> 安装 pip install celery #pip install flower #celery的及时监控组件配置 默认celery的配置文件名是celeryconfig.py,本例的内容如下: BROKER_URL amqp://guest:guest[localhost](https://my.oschina.net/u…...