数据结构(邓俊辉)学习笔记】优先级队列 06——完全二叉堆:批量建堆
文章目录
- 1. 自上而下的上滤:算法
- 2. 自上而下的上滤:效率
- 3. 自下而上的下滤:算法
- 4. 自下而上的下滤:实例
- 5. 自下而上的下滤:效率
1. 自上而下的上滤:算法
在介绍过完全二叉堆标准的静态和动态操作接口之后,我们接下来讨论如何批量地建造一个堆。也就说对于任给的 n 个元素,我们希望将它构建成一个堆。这样一个过程也称作Heapification。

在完全二叉堆模板类中,我们可以找到这样一个构造函数,其功能是以任意指定的规模为 n 的数组 a 为蓝本,将其中的元素组成一个完全二叉堆。为此我们首先需要调用向量的 copyfrom 接口,将这个数组复制到内部,而实质的操作则是调用 heapify 这个算法将这个元素调整为堆。那么这个 heapify 算法又当如何实现呢?
如果不在乎计算成本,这算不上是一个难题,甚至我们有现成的解决方案。比如这就是(上图所示)一个现成的解决方案,我们称这个方案为蛮理算法,因为它的思路是直截了当的,也就是逐个地将每一个元素通过完全二叉堆标准的 insert() 接口插入其中。
比如一种相对更为紧凑的实现方式是这样: 为此我们只需按照层次遍历的次序,也就是自上而下、自左而右,逐一的对每一个元素做上滤处理。每经过这样一套上滤,就等效于插入了一个新节点,而当所有的节点都经过如此上滤之后,整个堆也就自然建成了。
来看一个具体的实例,考察一个由5个元素所构成的初始向量。当然这里画出的是在逻辑上与之对应的那棵完全二叉树。
- 我们首先来考虑根节点:作为第一个节点,它所对应的上滤是一个平凡的情况,实际上它只需原地不动,我们就可以认为已经将它作为第一个元素插入了这个堆。也就是说,我们直接就得到了一个元素构成的堆。正因为这一部并没有任何实质的动作,因此我们不妨将其忽略掉,而直接从编号为 1 的元素开始。在我们的图中,也就是这个灰色的节点。相对于当前的堆,这个节点恰好就是末元素。因此根据我们此前的插入算法,在对它进行一次上滤调整之后,就可以顺利地将它插入到堆中,于是我们就可以得到一个规模为2的堆。
- 再接下来需要考察2号,也就是这个灰色的节点(上图下排倒数右二)。同样地,相对于当前的堆,它也恰好是末元素。因此我们也只需对它做一趟上滤,即可将它插入到当前的堆中。于是这个堆的规模又将从2拓展至3。
- 接下来的故事都类似,也就是我们需要进而去考察编号为3的节点,并且通过一趟上滤将它插入到当前的堆中,从而使得堆的规模进而上升为4。
- 最后一个节点也是如此,我们也只需对它做一趟上滤,就可将它插入到当前这个堆中。
- 从而最终得到一个规模为5的堆。
从逐一插入各节点的角度来看,这个算法平淡无奇,其正确性也因此显而易见。那么这个简明的算法效率又如何呢?
2. 自上而下的上滤:效率
就从最坏情况的角度对上述算法的效率来做一分析。

回顾刚才的蛮力算法,我们需要自上而下、自左而右的处理每一个节点,而每一个节点都要相应地做一次上滤,因此我们也将这种建堆的模式称作自上而下的上滤。
不难看出,在最坏的情况下,每个节点都有可能要一直上滤至根节点,其对应的计算成本也应该线性正比于其深度,因此总体的时间成本也应该就是每一个节点深度的总和。
当然也可以精确地对此做一个计算。但实际上我们只需要对其中的部分节点进行计算就足以验证这个算法是低效的,比如我们不妨来考虑那些所有底层的叶节点。我们知道,在完全二叉树中,至少有一半节点是页节点,而且在渐进意义上它们的深度都是log(n)。
因此就大 O 记号而言,仅这部分节点所消耗的时间就会多达 n log(n)。没错,多达 n log(n)。我们认为这是一个不可接受的效率。为什么这么讲呢?应该记得,我们设计和实现优先级队列的最初动机在于,我们需要代价足够低廉,同时又能维护所有元素之间偏序关系的一种数据结构。没错,偏序。
而我们早已知道,在多达 n log(n) 的时间之内,我们完全可以对所有元素做全排序。是的,用 n log(n)的时间,得到了一个超出我们所预期的,更强的功能。因此反过来,如果我们只是满足于偏序,而无需考虑全序的话,或许应该能够指望成本更加低廉。好消息是,事实的确如此。
3. 自下而上的下滤:算法
为了得到改进的建堆算法,我们需要来考察这样一个典型的场景:

假设已经有了两个初始的堆,H0和 H1,而它们的堆顶 R0和 R1分别作为第三个节点 p 的左和右孩子。对于这样一种情况,在这种情况下,我们应该如何迅速地将这两个堆合并起来,从而构成一个更大的堆呢?实际上这并不是一个什么新问题,你能看得出来吗?
是的,完全二叉堆的 delMax 算法。在这个算法中,我们首先要将最大元,也就是堆顶摘除掉,并且用向量中当前的末元素来取而代之。是的,我们就来考察刚刚取而代之的那个时刻。
在那样一个时刻,难道不恰好就是我们所说的这样一个场景吗?我们来验证一下,作为此前完全二叉堆的一部分,它们依旧处处满足堆序性和结构性。因此,它们都各自成为一个堆。同时,它们也是新的这个根节点的子堆。如果你能看透这一点,也就自然可以得到相应的调整算法。
没错,我们只需套用此前 delMax 算法的后半部分。具体来说,就是对这个新的根节点做一次下滤:当然,下滤的方向可能有两个:或者沿着左路的分支一直进入到左侧的这个子堆,也可能沿着右侧的分支进入到右侧的这个子堆。
无论如何,一旦节点 p 的下滤得以完成,原先的两个子堆也自然的就完成了合并。将这种处理手法退而广之,并反复使用,我们就可以得到一个效率更高的批量建堆算法。这个算法出自于 floyd 之手,该算法处理各节点的次序与此前的蛮力算法恰好相反,也就是说,在树中应该是自下而上,自右而左逐个处理。而对于每一个节点,我们都只需做一趟下滤。 当然,对于叶子节点而言,下滤并没有实际的意义,因此这个算法只需考虑所有的内部节点。相应地,第一个接受处理的也应该是最后一个内部节点。
如果全堆的规模为 n,那么这个最末尾的内部节点在向量中所对应的秩就应该是 floor(n/2) - 1。我们刚才已经看到,对每一个内部节点实施的下滤,其实质效果等同于将左右子堆合并起来,因此这样一个自下而上,逐个下滤的过程也就等效于各子堆逐层向上合并,规模不断增加的过程。
因此最终当根节点的下滤也完成之后,所有的节点也自然地从整体上构成了一个完全二叉堆。
4. 自下而上的下滤:实例

来看这样一个实例,这是由9个节点所构成了一棵完全二叉树,因此在物理上的向量中,最末尾内部节点所对应的秩应该为 floor(9/2) - 1 = 3,也就是这个节点(上图节点3)。不要误解这里它的数值也为3,纯属巧合。
请注意,在初始情况下,无论如何,每一个业节点都可以认为是自成为一个子堆,因此在此局部恰好就构成了我们此前所说的那样一个典型的模式:局部的子树根以及下属的左和右两个子堆,我们的任务是将这两个子堆合二为一。
- 算法非常简明,为此只需对局部的子树根节点 3 做一次下滤,下滤的结果是这样:可以看到,不出意外,我们的确完成了两个子堆的合并。
- 接下来该轮到再往前一个的内部节点,也就是6。在此,我们又一次看到了这个典型的模式:一个局部的子树根,以及左右两个子堆,同样地,我们只需对局部子树根 6 做一次下滤,即可将此局部调整为一个更大规模的堆,就像这样(上图下左1)。
- 再接下来,应该轮到内部节点 1,请注意,这里依然是一个我们算法可以处理的模式:一个局部的子树根,以及左右两个待合并的子堆。依然套用现成的算法,我们只需对局部子树根1做一次下滤,即可完成局部的合并。合并的结果是这样(上图下左1)。
- 好,最终应该轮到全树的根节点2,此时我们依然可以看到这样一个算法可以处理的典型模式:根节点,以及左右两个待合并的子堆。对于我们的处理手法,你应该现在非常熟悉了:只需对根节点做一次下滤,即可完成整体的合并。最终的结果是这样:不出意外,我们的确得到了一个由所有元素构成的完全二叉堆。
这个算法的正确性也同样显而易见,那么它的效率究竟有多高呢?是否如我们所预期的那样,严格的优于此前的 n log(n)呢?
5. 自下而上的下滤:效率

纵观 Floyd 建堆算法,实质的计算成本来自于对每个节点的下滤。
我们可以看到,每一个节点都会经过一系列的交换,下降一定的高度,有的下降得少一些,有的下降得多一些。就最坏情况而言,每个内部节点所下降的层次数至多不过它最初的高度,因此整个Floyd 算法的计算成本无非就是每一个节点所对应高度的总和。 经过推算不难得知,这个总和在渐进意义上无非是 O(n) ,限于时间关系,在此不妨省去详细地推导过程。而利用节省下来的这部分时间,我们不妨就时间效率将Floyd 算法和此前的蛮力算法来做一对比:这一对比既有趣,也更有意义。
应该记得蛮力算法O(n log(n) )的效率是来源于对所有节点深度的求和。 是的,这里的差异就在于究竟是对高度求和还是对深度求和。而饶有趣味的一个问题是,分别采用这样两个貌似非常接近的指标来进行求和,为什么在渐进的意义上却有巨大的差异呢?对于这一现象,你又当如何解释呢?
没错,造成这种实质差异的根本原因就在于,在完全二叉树中,越是靠近底层,节点越多;而越是靠近顶层,节点的就越少。因此,如果以深度作为成本的指标,那么累计的总和也自然会更大。
打个未必恰当的比方,每一个完全二叉堆就犹如一个社会,如果将高度对应于收入的水平,那么高收入人群必然是凤毛麟角,而大部分都是中低收入者。而如果需要对所有的人征税,再自然不过的规则莫过于按照收入的高低来决定税收的比例。低收入者少纳税,高收入者多纳税,再合理不过了。
~
事实上Floyd 算法所对应的正是这样一种合理的税收政策,从这个角度来看,蛮力算法恰好颠倒了标准。就算法相当于为了迎合少数的富人,居然以收入作为反比来确定税赋的比例。因此,自然会不得人心,并最终受到惩罚。
相关文章:
数据结构(邓俊辉)学习笔记】优先级队列 06——完全二叉堆:批量建堆
文章目录 1. 自上而下的上滤:算法2. 自上而下的上滤:效率3. 自下而上的下滤:算法4. 自下而上的下滤:实例5. 自下而上的下滤:效率 1. 自上而下的上滤:算法 在介绍过完全二叉堆标准的静态和动态操作接口之后…...
Java | Leetcode Java题解之第344题反转字符串
题目: 题解: class Solution {public void reverseString(char[] s) {int n s.length;for (int left 0, right n - 1; left < right; left, --right) {char tmp s[left];s[left] s[right];s[right] tmp;}} }...
定制开发AI智能名片O2O商城小程序:基于限量策略与个性化追求的营销创新
摘要:随着科技的飞速发展和消费者需求的日益多元化,传统商业模式正经历着前所未有的变革。在数字化转型的大潮中,定制开发AI智能名片O2O商城小程序作为一种新兴的商业模式,凭借其独特的个性化定制能力、高效的线上线下融合(O2O&am…...
Spring MVC Controller返回json日期格式配置失效的解决办法
如题,Spring MVC 4.3.0版本,配置jackson读写json。Controller层方法返回值对象包含java.util.Date类型的属性,并且在applicationContext.xml中配置了jackson的日期格式: <mvc:annotation-driven><mvc:message-converters…...
3.Default Constructor的构造操作
目录 1. 问题引入 2. 4种implicitly声明的default constructor 1. 问题引入 “default constructors......在需要的时候被编译产生出来”。关键词是“在需要的时候”,被谁需要,做什么事情?看看下面的代码,然后梳理下思路。 cl…...
CSS的:current伪类:精准定位当前活动元素
CSS(层叠样式表)是控制网页样式的核心语言。随着CSS4的提出,一系列新的选择器被引入,其中:current伪类便是这些新特性之一。:current伪类允许开发者选择当前处于活动状态的元素,这在创建动态和交互性网页时非常有用。本…...
搭建个人网站
一 个人搭建网站需要进行的操作 详细步骤: 1 网站目标:搭建在线查看法拍房拍卖价格的预测模型,输出预测结果 2 实际功能:在线爬取 阿里法拍网站的信息 3 根据实时模型建模预测法拍价格和成交概率 要搭建一个能够在线查看法拍房拍卖…...
机器学习课程学习周报八
机器学习课程学习周报八 文章目录 机器学习课程学习周报八摘要Abstract一、机器学习部分1.1 self-attention的计算量1.2 人类理解代替自注意力计算1.2.1 Local Attention/Truncated Attention1.2.2 Stride Attention1.2.3 Global Attention1.2.4 聚类Query和Key 1.3 自动选择自…...
福泰轴承股份有限公司进销存系统pf
TOC springboot413福泰轴承股份有限公司进销存系统pf 绪论 1.1 研究背景 现在大家正处于互联网加的时代,这个时代它就是一个信息内容无比丰富,信息处理与管理变得越加高效的网络化的时代,这个时代让大家的生活不仅变得更加地便利化&#…...
【k8s从节点报错】error: You must be logged in to the server (Unauthorized)
k8s主节点可以获取nodes节点信息,但是从节点无法获取,且报错“error: You must be logged in to the server (Unauthorized)” 排查思路: 当时证书过期了,只处理的主节点的证书过期,没有处理从节点的 kubeadm alpha …...
风清扬/基于Java语言的光伏监控系统+光伏发电预测+光伏项目+光伏运维+光伏储能项目
基于Java语言的光伏监控系统光伏发电预测光伏项目光伏运维光伏储能项目 介绍 基于Java语言的光伏监控系统光伏发电系统光伏软件系统光伏监控系统源码光伏发电系统源码 基于Java语言的光伏监控系统光伏发电预测光伏项目光伏运维光伏储能项目 安装教程 参与贡献 Fork 本仓库新…...
Datawhale X 魔搭 AI夏令营第四期 魔搭-AIGC方向全过程笔记
task1: 传送门 task2: 传送门 task3: 传送门 目录 Task1 赛题内容 可图Kolors-LoRA风格故事挑战赛 baseline要点讲解(请配合Datawhale速通教程食用) Step1 设置算例及比赛账号的报名和授权 Step2 进行赛事报名并创建PAI实例 Step3 执行baseline Step4…...
数组---怎么样定义和引用数组
一怎么定义数组 例 int a[10]; //定义了一个一维数组,数组名为a,此数组包含10个整型元素 所以我们了解到数组的基本定义为 类型符 数组名 [常量表达式] 定义数组可以包括常量和符号常量如 int [ 35 ];但是不能利用变量定义如 int n; …...
Nginx—Rewrite
目录 一、Nginx—Rewrite概述 1、常用的Nginx正则表达式 2、Rewrite功能 3、Rewrite跳转实现 4、Rewrite执行顺序和语法格式 二、location概述 1、location分类 2、location 常用的匹配规则 3、location 优先级 案例一: 案例二: 案例三&…...
《深入浅出WPF》读书笔记.5控件与布局(上)
《深入浅出WPF》读书笔记.5控件与布局(上) 背景 深入浅出WPF书籍学习笔记附代码。WPF中数据是核心是主动的,UI是数据的表达是被动的。 程序的本质是数据算法;控件的本质是数据行为; 5.控件与布局 一、6类控件派生关系 1.布局控件:可以容纳多个控件…...
二叉树的判断
二叉树的判断 判断一颗二叉树是不是搜索二叉树 (左边的比根小,右边的比根大) 中序遍历一下,如果是的话就一定是升序的 如何判断一颗二叉树是否是完全二叉树 1.遍历任意的节点时候,如果返回右孩子没有左孩子&#x…...
Hive3:常用的内置函数
1、查看函数列表 -- 查看所有可用函数 show functions; -- 查看count函数使用方式 describe function extended count;2、数学函数 -- round 取整,设置小数精度 select round(3.1415926); -- 取整(四舍五入) select round(3.1415926, 4); -- 设置小数精度4位(四…...
设计模式---构建者模式(Builder Pattern)
构建者模式(Builder Pattern) 是一种创建型设计模式,旨在将复杂对象的构建过程与其表示分离。它允许使用相同的构建过程创建不同的表示。该模式通常用于构建复杂对象,这些对象由多个部分组成或具有多个可选属性。 构建者模式的核…...
Pytorch中transform的应用
在PyTorch中,transforms模块主要用于对图像进行预处理和数据增强,以便于训练深度学习模型。这些转换操作可以包括裁剪、缩放、旋转、翻转等,以及对图像进行标准化处理。下面将详细介绍一些常用的transforms操作及其应用。 1. 常用的transfor…...
okular阅读软件简介
okular阅读软件官网:https://okular.kde.org/zh-cn/ Okular 是一款由 KDE 开发的跨平台文档阅读器,以其功能丰富、轻巧快速而著称。它支持多种文件格式,包括 PDF、EPub、DjVu、MD 文档,以及 JPEG、PNG、GIF、Tiff 和 WebP 图像&a…...
汽车生产虚拟实训中的技能提升与生产优化
在制造业蓬勃发展的大背景下,虚拟教学实训宛如一颗璀璨的新星,正发挥着不可或缺且日益凸显的关键作用,源源不断地为企业的稳健前行与创新发展注入磅礴强大的动力。就以汽车制造企业这一极具代表性的行业主体为例,汽车生产线上各类…...
OpenPrompt 和直接对提示词的嵌入向量进行训练有什么区别
OpenPrompt 和直接对提示词的嵌入向量进行训练有什么区别 直接训练提示词嵌入向量的核心区别 您提到的代码: prompt_embedding = initial_embedding.clone().requires_grad_(True) optimizer = torch.optim.Adam([prompt_embedding...
使用Spring AI和MCP协议构建图片搜索服务
目录 使用Spring AI和MCP协议构建图片搜索服务 引言 技术栈概览 项目架构设计 架构图 服务端开发 1. 创建Spring Boot项目 2. 实现图片搜索工具 3. 配置传输模式 Stdio模式(本地调用) SSE模式(远程调用) 4. 注册工具提…...
Linux nano命令的基本使用
参考资料 GNU nanoを使いこなすnano基础 目录 一. 简介二. 文件打开2.1 普通方式打开文件2.2 只读方式打开文件 三. 文件查看3.1 打开文件时,显示行号3.2 翻页查看 四. 文件编辑4.1 Ctrl K 复制 和 Ctrl U 粘贴4.2 Alt/Esc U 撤回 五. 文件保存与退出5.1 Ctrl …...
探索Selenium:自动化测试的神奇钥匙
目录 一、Selenium 是什么1.1 定义与概念1.2 发展历程1.3 功能概述 二、Selenium 工作原理剖析2.1 架构组成2.2 工作流程2.3 通信机制 三、Selenium 的优势3.1 跨浏览器与平台支持3.2 丰富的语言支持3.3 强大的社区支持 四、Selenium 的应用场景4.1 Web 应用自动化测试4.2 数据…...
tomcat入门
1 tomcat 是什么 apache开发的web服务器可以为java web程序提供运行环境tomcat是一款高效,稳定,易于使用的web服务器tomcathttp服务器Servlet服务器 2 tomcat 目录介绍 -bin #存放tomcat的脚本 -conf #存放tomcat的配置文件 ---catalina.policy #to…...
【实施指南】Android客户端HTTPS双向认证实施指南
🔐 一、所需准备材料 证书文件(6类核心文件) 类型 格式 作用 Android端要求 CA根证书 .crt/.pem 验证服务器/客户端证书合法性 需预置到Android信任库 服务器证书 .crt 服务器身份证明 客户端需持有以验证服务器 客户端证书 .crt 客户端身份…...
STM32标准库-ADC数模转换器
文章目录 一、ADC1.1简介1. 2逐次逼近型ADC1.3ADC框图1.4ADC基本结构1.4.1 信号 “上车点”:输入模块(GPIO、温度、V_REFINT)1.4.2 信号 “调度站”:多路开关1.4.3 信号 “加工厂”:ADC 转换器(规则组 注入…...
GraphRAG优化新思路-开源的ROGRAG框架
目前的如微软开源的GraphRAG的工作流程都较为复杂,难以孤立地评估各个组件的贡献,传统的检索方法在处理复杂推理任务时可能不够有效,特别是在需要理解实体间关系或多跳知识的情况下。先说结论,看完后感觉这个框架性能上不会比Grap…...
python打卡第47天
昨天代码中注意力热图的部分顺移至今天 知识点回顾: 热力图 作业:对比不同卷积层热图可视化的结果 def visualize_attention_map(model, test_loader, device, class_names, num_samples3):"""可视化模型的注意力热力图,展示模…...
