堆和优先队列

news2025/1/10 10:11:22

文章目录

      • 维护堆的性质
      • 建堆
      • 堆排序算法
    • 优先队列详解
      • cpp标准库 priority_queue
    • 参考文献

虽然“堆”这个词源自堆排序,但是目前它已经被引申为“垃圾存储机制”,例如在Java和Lisp语言中所定义的。强调一下,我们使用的堆不是垃圾收集存储,并且在本文的任何部分,只要涉及堆,指的都是堆数据结构, 而不是垃圾收集存储。

堆是一个数组,它可以被看成是一个近似的完全二叉树。故堆经常也被称为二叉堆。堆和完全二叉树的不同点在于堆的最底层可能不是充满的,即除了最底层外,该树是完全充满的,而且是从左向右填充。

表示堆的数组A包括两个属性:A.length通常给出数组元素的个数, A.heap-size表示有多少个堆元素存储在该数组中。也就是说,虽然A[1…A.length]可能都存有数据,但只有A[1…A.heap-size]中存放的是堆的有效元素,这里0<=A.heap-size<=A.length. 树的根节点是A[1],这样给定一个节点的下标i, 我们很容易计算得到它的父节点、左孩子和右孩子的下标:

下标为i的结点的父亲节点的下标为:PARNET(i) = ⌊ i / 2 ⌋ \lfloor i/2\rfloor i/2.

结点i的左孩子的下标为LEFT(i) = 2i, 右孩子的下标为RIGHT(i) = 2i+1;

注:完全二叉树的每个叶节点高度高度,也每个内部节点均有2个孩子。

二叉堆可以分为两种形式:最大堆和最小堆。在这两种堆中,结点的值都要满足堆的性质。

在最大堆中,最大堆性质是指出了根以外的所有结点i,都要满足: A[PARENT(i)]>=A[i]. 即除了根节点以外的所有结点的值至多与其父结点一样大。因此,最大堆中的最大元素存放在根节点中。最小堆的组织形式恰好相反。

堆中结点的高度: 如果把堆看成是一棵树,我们定义堆中一个结点的高度就为该结点到叶结点最长简单路径上边的数目;进而我们可以把堆的高度定义为根节点的高度。

我们要掌握如何建堆,如何调整,如何提取最大元素。

维护堆的性质

MAX-HEAPIFY是用于维护最大堆性质的重要过程。它的输入为一个数组A和一个下标i。在调用MAX-HEAPIFY的时候,**我们假定根节点为LEFT(i)和RIGHT(i)的二叉树都是最大堆,**但这时A[i]有可能小于其他孩子,这样就违背了最大堆的性质。MAX-HEAPIFY通过让A[i]的值在最大堆中“逐级下降”,从而使得下标i为根节点的子树重新遵循最大堆的性质。

MAX-HEAPIFY(A, i)
1 l = LEFT(i)
2 r = RIGHT(i)
3 if l<=A.heap-size and A[l]>A[i]
		largest = l
	else
		larest = i
	if r<=A.heap-size and A[r]>A[largest]
		largest = r
	if largest \ne i
		exchange A[i] with A[largest]
		MAX-HEAPIFY(A, largest)

对于一个树高为h的节点来说, MAX-HEAPIFY的时间复杂度是O(h).

建堆

我们可以用自底向上的方法利用过程MAX-HEAPIFY把一个大小为n = A.length的数组A[1…n]转化为最大堆。可知,当用数组存储n个元素的堆时,叶节点下标分别是 ⌊ n / 2 ⌋ + 1 , ⌊ n / 2 ⌋ + 2 , … , n \lfloor n/2\rfloor+1,\lfloor n/2\rfloor+2,\dots, n n/2+1,n/2+2,,n. (由于n的父节点的下标为 ⌊ n / 2 ⌋ \lfloor n/2\rfloor n/2, 且PARENT(n)的下一个节点即为第一个叶节点,故叶节点下标分别是 ⌊ n / 2 ⌋ + 1 , ⌊ n / 2 ⌋ + 2 , … , n \lfloor n/2\rfloor+1,\lfloor n/2\rfloor+2,\dots, n n/2+1,n/2+2,,n. )每个叶节点都可以看成只包含一个元素的堆。过程BUILD-MAX-HEAP对树中的其他结点都调用一次MAX-HEAPIFY.

BUILD-MAX-HEAP(A)
1 A.heap-size = A.length
2 for i = ⌊ A . l e n g t h / 2 ⌋ \lfloor A.length /2 \rfloor A.length/2 downto 1

3 MAX-HEAPIFY(A, i)

时间复杂度为O(n).

堆排序算法

初始时候,堆排序算法利用BUILD-MAX-HEAP将输入数组A[1…n]建成最大堆,其中n = A.length. 因为数组中的最大元素总在根节点A[1]中,通过把它与A[n]进行互换,我们可以让该元素放到正确的位置。这时候,如果我们从堆中去掉结点n(这一操作可以通过减少A.heap-size的值来实现),剩余的结点中,原来根的孩子结点仍然是最大堆,而新的根节点可能会违背最大堆的性质。为了维护最大堆的性质,我们要做的是调用MAX-HEAPIFY(A, 1), 从而在A[1…n-1]上构造一个新的最大堆。堆排序算法会不断重复这一个过程,直到堆的大小从n-1降到2.

HEAPSORT(A)
1 BUILD-MAX-HEAP(A)
2 for i = A.length downto 2
3 	exchange A[1] with A[i]
4 	A.heap-size -= 1
5		MAX-HEAPIFY(A,1)

时间复杂度为O(n logn).

优先队列详解

优先队列虽然也叫做队列,但是和队列的内部实现是不同的。优先队列的数据结构基础是堆,是通过堆来实现的。所以要想学会优先队列,首先要学会堆的思想。

优先队列有两种形式:最大优先队列和最小优先队列。

优先队列(priority queue)是一种用来维护由一组元素构成的集合S的数据结构,其中的每一个元素都有一个相关的值,称为关键字(key)。优先程度是基于关键字的。一个最大优先队列支持以下操作:

INSERT(S, x): 把元素x插入集合S中。这一操作等价于 S = S ∪ x S = S\cup {x} S=Sx.

MAXIMUM(S): 返回S中具有最大关键字的元素。

EXTRACT-MAX(S): 去掉并返回S中的具有最大关键字的元素。

INCREASE-KEY(S, x, k): 将元素x的关键字值增加到k, 这里假设k的值不小于x的原关键字值。 这里x可以是索引。

下面我们讨论如何实现最大优先队列的操作。

HEAP-MAXIMUM(A)

1 return A[1]
HEAP-EXTRACT-MAX(A)
1 if A.heap-size < 1
2 	error "heap underflow"
3 max = A[1]
4 A[1] = A[A.heap-size]
5 A.heap-size = A.heap-size - 1
5 MAX-HEAPIFY(A,1)
6 return max

HEAP-EXTRACT-MAX的时间复杂度为O(log n).

在优先队列中,我们希望增加关键字的优先队列元素由对应的数组下标来标识。

这一操作需要首先将元素A[i]的关键字更新为新值。因为增大A[i]的关键字可能会违反最大堆的性质,所以需要调整该元素的位置。当前元素会不断地与其父结点比较,如果当前元素的关键字较大,则当前元素与其父结点进行交换。这一过程会不断地重复,直到当前元素的关键字小于其父结点时终止,因为此时已经重新符合了最大堆的性质。

HEAP-INCREASE-KEY(A, i, key)
1 if(key<A[i])
2		error "new key is smaller than current key"
3 A[i] = key 
4 while i>1 and A[PARENT(i)] < A[i]
5		exchange A[i] with A[PARENT(i)]
6		i = A[PARENT(i)]

插入操作

MAX-HEAP-INSERT能够实现INSERT操作。它的输入是要被插入到最大堆A中的新元素的关键字。MAX-HEAP-INSERT首先通过增加一个关键字为 − ∞ -\infty 的叶节点来扩展最大堆。然后调用HEAP-INCREASE-KEY为新结点设置对应的关键字,同时保持最大堆的性质。

MAX-HEAP-INSERT(A, key)
1 A.heap-size = A.heap-size + 1
2 A[A.heap-size] = -\infty
3 HEAP-INCREASE-KEY(A, A.heap-size, key)

cpp标准库 priority_queue

cpp标准库priority_queue提供了优先队列的实现,优先队列定义于 https://cplusplus.com/reference/queue/ 这个头文件中。该头文件存在两个容器类,一个是queue, 另一个是priority_queue. 第一个是先进先出队列,第二个是优先队列。

优先队列是一类容器适配器(container adaptor), 专门设计来使得**它的第一个元素总是它所包含的“最优先“的那个元素,**优先的标准某种strict weak ordering 标准(严格弱序列标准)。

template<
    class T,
    class Container = std::vector<T>,
    class Compare = std::less<typename Container::value_type>
> class priority_queue;

上述是priority_queue的模板类定义。Compare提供了”优先”的定义。

关于Compare: 如果Compare的第一个参数位于第二个参数的前面,则返回true. 例如上述模板类的默认参数是std::less ,即数字小的位于前面,而优先队列的的第一个(front)元素是最大的元素,故在compare函数中位于前面的元素实际上后输出。即队列的front包含了根据Compare所规定的弱排序的位于后边的元素。仍然以less为例,2<3, 2位于3的前面。 故2后输出。可以形象地记忆为:位于小于号左边的人先来,先来的人在售票大厅门口等着,将前面的位置留给后来的德高望重的人。

在这里插入图片描述
总之,记忆一点:排序位于后面的元素是优先级更高的。

参考文献

[1] 算法导论 第6章 堆排序
[2] https://cplusplus.com/reference/queue/

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/680459.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

设计模式之访问者模式笔记

设计模式之访问者模式笔记 说明Iterator(访问者)目录访问者模式示例类图抽象访问者角色类抽象元素角色类宠物猫类宠物狗类自己类其他人类家类测试类 说明 记录下学习设计模式-访问者模式的写法。JDK使用版本为1.8版本。 Iterator(访问者) 意图:表示一个作用于某对象结构中的…

平面设计师实习报告范文十篇

平面设计师实习报告范文1 经过了四年的系统性理论学习&#xff0c;使我对平面设计有了基本掌握&#xff0c;对于平面设计这个专业也有了一个系统的了解和掌握。我们即将离开大学校园&#xff0c;作为平面设计专业的毕业生&#xff0c;心中想得更多的是如何去做好一个平面设计师…

ETCD集群运维

ETCD集群运维 ETCD扩容节点ETCD参数调优时间参数快照磁盘网络 监控维护压缩历史版本消除碎片化存储配额快照备份 灾难恢复快照恢复 etcd gateway作用什么时候不应该使用网关启动方式 gRPC代理可扩展的watch API租约请求缓存启动gRPC代理 ETCD扩容节点 节点1&#xff1a;10.1.1…

管理类联考——逻辑——知识篇——论证推理——一、削弱——haimian

削弱 考点分析 削弱 年度 2012 2013 2014 2015 2016 2017 2018 2019 2020 2021 2022 2023题量223262132 主要问法 以下哪项如果为真&#xff0c;则能最严重地削弱&#xff08;反驳&#xff09;上述结论?以下哪项如果为真&#xff0c;则最能削弱&#xff08;反驳&#xff09…

文心一言 VS 讯飞星火 VS chatgpt (46)-- 算法导论6.1 4题

四、假设一个最大堆的所有元素都不相同&#xff0c;那么该堆的最小元素应该位于哪里? 文心一言&#xff1a; 最大堆的最小元素位于堆的根结点。由于最大堆的性质&#xff0c;根结点是堆中所有元素的最大值&#xff0c;因此它也是堆中所有元素的最小值。 讯飞星火&#xff1a…

工具及方法 - 如何进行任务管理

如何做好你该做的事&#xff0c;这是你的一生之敌。 没有一步到位的工具和一劳永逸的方法&#xff0c;只有不断变化的需求和自己用起来顺手的感觉。 对于GTD工具&#xff0c;在手机上安装过番茄工作法的工具&#xff0c;在电脑的Excel上也做过任务列表&#xff0c;在免费的项目…

解决 kali换源之后签名无效

报错问题&#xff1a; apt-get update 报错 更新扩展知识&#xff1a;kali更新源 终端输入 vi /etc/apt/sources.list #中科大 deb http://mirrors.ustc.edu.cn/kali kali-rolling main non-free contrib deb-src http://mirrors.ustc.edu.cn/kali kali-rolling main …

计算机视觉中,有哪些基于控制点对的图像变换?

这里探讨的所有图像变换&#xff08;二维&#xff09;都是基于控制点对的&#xff0c;它们的主要区别在于&#xff1a; 1、它们是如何通过两张图像的控制点对产生变换场&#xff08;变换矩阵或者变换公式中的参数&#xff09;的 2、控制点之间的对应关系严格程度 这里说的变换…

远心镜头案例锦集

远心镜头&#xff08;Telecentric lens&#xff09; 定义&#xff1a;远心镜头是一类将其入瞳或出瞳放置于无穷远的光学系统。是为纠正传统镜头视差而设计&#xff0c;它可以在一定的物距范围内&#xff0c;使得到的图像放大倍率不会变化&#xff0c;即这种镜头拍出来的图像没…

人工智能(4):模型评估

模型评估是模型开发过程不可或缺的一部分。它有助于发现表达数据的最佳模型和所选模型将来工作的性能如何。 按照数据集的目标值不同&#xff0c;可以把模型评估分为分类模型评估和回归模型评估。 1 分类模型评估 准确率 预测正确的数占样本总数的比例。 其他评价指标…

嵌入式C开发 VS 嵌入式CPP开发!

目录 ​答主&#xff1a;听心跳的声音 答主&#xff1a;pansz 答主&#xff1a;candy 这是知乎嵌入式领域的一个热门话题&#xff0c;原文链接&#xff1a; https://www.zhihu.com/question/374663834 几个高赞回答&#xff1a; ​答主&#xff1a;听心跳的声音 单片机的主…

多项式回归的原理及实现、多重回归的原理

1.多项式回归的原理及实现 笔记来源于《白话机器学习的数学》 1.1 多项式回归的原理 预测一个变量 x x x与一个变量 y y y的关系 例如&#xff1a;广告费 x x x与点击量 y y y 用曲线拟合数据 求导过程类比本人之前的博客进行推导&#xff0c;相关笔记&#xff1a;最小二乘法的…

Nginx基于授权的访问控制步骤

目录 一、安装httpd-tools 二、生成用户密码认证文件 三、修改主配置文件相对应的目录&#xff0c;添加认证配置置顶 四、 重启服务 五、 访问网址 一、安装httpd-tools 二、生成用户密码认证文件 三、修改主配置文件相对应的目录&#xff0c;添加认证配置置顶 Vim /usr/loc…

【雕爷学编程】Arduino动手做(124)---24位WS2812环形灯板

37款传感器与执行器的提法&#xff0c;在网络上广泛流传&#xff0c;其实Arduino能够兼容的传感器模块肯定是不止这37种的。鉴于本人手头积累了一些传感器和执行器模块&#xff0c;依照实践出真知&#xff08;一定要动手做&#xff09;的理念&#xff0c;以学习和交流为目的&am…

tensorflow2模型保存和恢复

有两种方法可以保存模型&#xff1a; 使用检查点&#xff0c;一种简单的在硬盘上保存变量的方法使用SavedModel&#xff0c;模型结构及检查点 检查点不包含任何关于模型自身的描述&#xff1a;它们只是一种简单的存储参数并能让开发者正确恢复它的方法。 SavedModel格式在保…

Mysql复习多表查询

Mysql复习多表查询 1.多表关系2.多表查询概述3.内连接4. 外连接5. 自连接5.1 案例 6. 子查询6.1 标量子查询6.1.1 标量子查询案例 6.1 列子查询6.2 行子查询6.2.1 demo1 6.3 表子查询6.3.1 demo16.3.2 demo2 7.联合查询8.1 案例 附录 1.多表关系 >多表查询 项目开发中&…

【算法】代码随想录、数组——长度最小的子数组、滑动窗口实现

209.长度最小的子数组 解法思想来自代码随想录&#xff1a;209.长度最小的子数组 &#xff08;1&#xff09;暴力解法 我们暴力解法直接使用两个for循环&#xff0c;然后不断的遍历寻找符合条件的子序列&#xff1b; 初始化长度变量length和结果变量result为0和int类型最大数…

近期离职心情记录 大不了前端换行!

提了离职了&#xff0c;发消息给领导的时候我都不敢看&#xff0c;发了马上关闭了聊天框当乌龟。。。 一、大致背景介绍 现在在二线城市的上市公司&#xff0c;本人大专学历&#xff0c;学的是java&#xff08;甚至还报名了培训班&#xff09;。第一个公司是现在公司的外包公司…

管理类联考——逻辑——知识篇——论证推理——七、论证方式——haimian

论证方式 考点分析 削弱 年度 2012 2013 2014 2015 2016 2017 2018 2019 2020 2021 2022 2023题量211111 题型分类 关键问题 题型特征 典型问法&#xff1a; 为了评价上述论证的正确性&#xff0c;回答以下哪个问题最为重要以下哪项对于评价上述结论最为重要? 思路点拨…

【IMX6ULL驱动开发学习】10.设置uboot使用网络加载zImage和dtb

1. 在uboot中设置网络 首先启动自己的板子&#xff0c;使用 ifconfig 命令或者 ifconfig -a 命令查看自己网卡的地址 ifconfig ifconfig -a我的网卡地址是这个 52:15:66:2E:16:71接着设置自己的ip setenv ipaddr 192.168.1.100设置服务器ip setenv serverip 192.168.1.200保…