0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

五张图带你体会堆算法

算法与数据结构 来源:CSDN博客 作者:NoMasp柯于旺 2022-11-10 09:29 次阅读
加入交流群
微信小助手二维码

扫码添加小助手

加入工程师交流群

什么是堆

堆(heap),是一类特殊的数据结构的统称。它通常被看作一棵树的数组对象。在队列中,调度程序反复提取队列中的第一个作业并运行,因为实际情况中某些时间较短的任务却可能需要等待很长时间才能开始执行,或者某些不短小、但很重要的作业,同样应当拥有优先权。而堆就是为了解决此类问题而设计的数据结构。

二叉堆是一种特殊的堆,二叉堆是完全二叉树或者近似完全二叉树,二叉堆满足堆特性:父节点的键值总是保持固定的序关系于任何一个子节点的键值,且每个节点的左子树和右子树都是一个二叉堆。

当父节点的键值总是大于任何一个子节点的键值时为最大堆,当父节点的键值总是小于或等于任何一个子节点的键值时为最小堆。

为了更加形象,我们常用带数字的圆圈和线条来表示二叉堆等,但其实都是用数组来表示的。如果根节点在数组中的位置是1,第n个位置的子节点则分别在2n和2n+1位置上。

如下图所描的,第2个位置的子节点在4和5,第4个位置的子节点在8和9。所以我们获得父节点和子节点的方式如下:

PARENT(i)

1 return 小于或等于i/2的最大整数

LEFT-CHILD(i)

1 return 2i

RIGHT-CHILD(i)

1 return 2i+1

00c232ac-6096-11ed-8abf-dac502259ad0.png

假定表示堆的数组为A,那么A.length通常给出数组元素的个数,A.heap−size表示有多少个堆元素存储在该数组中。这句话略带拗口,也就是说数组A[1...A.length]可能都有数据存放,但只有A[1...A.heap−size]中存放的数据才是堆中的有效数据。毫无疑问0≤A.heap−size≤A.length。

最大堆除了根以外所有结点i都满足:A[PARENT(i)]≥A[i]。

最小堆除了根以外所有结点i都满足:A[PARENT(i)]≤A[i]。

一个堆中结点的高度是该结点到叶借点最长简单路径上边的数目,如上图所示编号为4的结点的高度为1,编号为2的结点的高度为2,树的高度就是3。

包含n个元素的队可以看作一颗完全二叉树,那么该堆的高度是Θ(lgn)。

通过MAX-HEAPIFY维护最大堆

程序中,不可能所有的堆都天生就是最大堆,为了更好的使用堆这一数据结构,我们可能要人为地构造最大堆。

如何将一个杂乱排序的堆重新构造成最大堆,它的主要思路是:

从上往下,将父节点与子节点以此比较。如果父节点最大则进行下一步循环,如果子节点更大,则将子节点与父节点位置互换,并进行下一步循环。注意父节点要与两个子节点都进行比较。

00d8ab54-6096-11ed-8abf-dac502259ad0.png

如上图说描述的,这里从结点为2开始做运算。先去l为4,r为5,将其与父节点做比较,发现左子节点比父节点更大。因此将它们做交换,设4为最大的结点,并继续以结点4开始做下一步运算。

因此可以给出伪代码如下:

MAX-HEAPIFY(A,i)

1 l=LEFT-CHILD(i)

2 r=RIGHT-CHILD(i)

3 if l<=A.heap-size and A[l]>A[i]

4 largest=l

5 else

6 largest=i

7 if r<=A.heap-size and A[r]>A[largest]

8 largest=r

9 if largest != i

10 exchange A[i] with A[largest]

11 MAX-HEAPIFY(A,largest)

在以上这些步骤中,调整A[i]、A[l]、A[r]的关系的时间代价为Θ(1),再加上一棵以i的子节点为根结点的子树上运行MAX-HEAPIFY的时间代价(注意此处的递归不一定会发生,此处只是假设其发生)。因为每个子节点的子树的大小至多为2n/3(最坏情况发生在树的底层恰好半满的时候)。因此MAX-HEAPIFY过程的运行时间为:

T(n)≤T(2n/3)+Θ(1)

也就是:

T(n)=O(lgn)

通过BUILD-MAX-HEAP构建最大堆

前面我们通过自顶向下的方式维护了一个最大堆,这里将通过自底向上的方式通过MAX-HEAPIFY将一个n=A.length的数组A[1...n]转换成最大堆。

回顾一下上面的图示,其总共有9个结点,取小于或等于9/2的最大整数为4,从4+1,4+2,一直到n都是该树的叶子结点,你发现了么?这对任意n都是成立的哦。

因此这里我们就要从4开始不断的调用MAX-HEAPIFY(A,i)来构建最大堆。

为什么会有这一思路呢?

原因是既然我们知道了哪些结点是叶子结点,从最后一个非叶子结点(这里是4)开始,一次调用MAX-HEAPIFY函数,就会将该结点与叶子结点做相应的调整,这其实也就是一个递归的过程。

00e6e8c2-6096-11ed-8abf-dac502259ad0.png

图示已经这么清晰了,就直接上伪代码咯。

BUILD-MAX-HEAP(A)

1 A.heap-size=A.length

2 for i=小于或等于A.length/2的最大整数 downto 1

3 MAX-HEAPIFY(A,i)

通过HEAPSORT进行堆排序算法

所谓的堆排序算法,先通过前面的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即可。

020a5e50-6096-11ed-8abf-dac502259ad0.png

上图的演进方式主要有两点:

1)将A[1]和A[i]互换,i从A.length一直递减到2

2)不断调用MAX-HEAPIFY(A,1)对剩余的整个堆进行重新构建

一直到最后堆已经不存在了。

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=A.heap-size-1

5 MAX-HEAPIFY(A,1)

优先队列

下一篇博文我们就会介绍大名鼎鼎的快排,快速排序啦,欢迎童鞋们预定哦~

话说堆排序虽然性能上不及快速排序,但作为一个尽心尽力的数据结构而言,其可谓业界良心呐。它还为我们提供了传说中的“优先队列”。

优先队列(priority queue)和堆一样,堆有最大堆和最小堆,优先队列也有最大优先队列和最小优先队列。

优先队列是一种用来维护由一组元素构成的集合S的数据结构,其中每个元素都有一个相关的值,称之为关键字(key)。

一个最大优先队列支持一下操作:

MAXIMUM(S)返回S中有着最大键值的元素。

EXTRACT−MAX(S)去掉并返回S中的具有最大键字的元素。
INCREASE−KEY(S,x,a)将元素x的关键字值增加到a,这里假设a的值不小于x的原关键字值。
INSERT(S,x)将元素x插入集合S中,这一操作等价于S=S∪{x}。

这里来举一个最大优先队列的示例,我曾在关于“50% CPU 占有率”题目的内容扩展 这篇博文中简单介绍过Windows的系统进程机制。

这里以图片的形式简单的贴出来如下:

022e0044-6096-11ed-8abf-dac502259ad0.png

在用堆实现优先队列时,需要在堆中的每个元素里存储对应对象的句柄(handle)。句柄的准确含义依赖于具体的应用程序,可以是指针,也可以是整型数。

在堆的操作过程中,元素会改变其在数组中的位置,因此在具体实现中,在重新确定堆元素位置时,就自然而然地需要改变其在数组中的位置。

一、前面的MAXIMUM(S)过程其实很简单,完全可以在Θ(1)时间内完成,因为只需要返回数组的第一个元素就可以呀,它已经是最大优先队列了嘛。

HEAP-MAXIMUM(A)

1 return A[1]

二、EXTRACT−MAX(S)就稍显复杂了一点,它的时间复杂度是O(lgn),因为这里面除了MAX-HEAPIFY(A,1)以外,其他的操作都是常量时间的。

HEAP-EXTRACT-MAX(A)

1 if A.heap-size < 1

2 error "堆下溢"

3 max=A[1]

4 A[1]=A[A.heap-size]

5 A.heap-size=A.heap-size-1

6 MAX-HEAPIFY(A,1)

7 return max

三、INCREASE−KEY(S,x,a)需要将一个大于元素x原有关键字值的a加到元素x上。

和上一个函数一样,首先判断a知否比原有的关键字更大。

然后就是老办法了,不断的将该结点与父结点做对比,如果父结点更小,那么就将他们进行对换。

相信有图示会更加清楚,于是……再来一张图。

023fa4ca-6096-11ed-8abf-dac502259ad0.png

HEAP-INCREASE-KEY(A,i,key)

1 if key < A[i]

2 error "新关键字值比当前关键字值更小"

3 A[i]=key

4 while i>1 and A[PARENT(i)] < A[i]

5 exchange A[i] with A[PARENT(I)]

6 i=PARENT(i)

在包含n个元素的堆上,HEAP-INCREASE-KEY的运行时间就是O(lgn)了。因为在第3行做了关键字更新的结点到根结点的路径长度为O(lgn)。

四、INSERT(S,x)首先通过一个特殊的关键字(比如这里的-10000扩展)结点来扩展最大堆,然后调用HEAP-INCREASE-KEY来为新的结点设置对应的关键字,同时保持最大堆的性质。

MAX-HEAP-INSERT(A,key)

1 A.heap-size=A.heap-sieze+1

2 A[A.heap-size]=-10000

3 HEAP-INCREASE-KEY(A,A.hep-size,key)

在包含n个元素的堆上,MAX-HEAP-INSERT的运行时间就是O(lgn)了。因为这个算法相对于上一个算法,除了HEAP-INCREASE-KEY之外就都是常量的运行时间了,而HEAP-INCREASE-KEY的运行时间我们在上一部分已经讲过了。

总而言之,在一个包含n个元素的堆中,所有优先队列的操作时间都不会大于O(lgn)。

---END---

审核编辑 :李倩

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 算法
    +关注

    关注

    23

    文章

    4760

    浏览量

    97134
  • 数据结构
    +关注

    关注

    3

    文章

    573

    浏览量

    41365
  • 数组
    +关注

    关注

    1

    文章

    420

    浏览量

    27114

原文标题:五张图带你体会堆算法

文章出处:【微信号:TheAlgorithm,微信公众号:算法与数据结构】欢迎添加关注!文章转载请注明出处。

收藏 人收藏
加入交流群
微信小助手二维码

扫码添加小助手

加入工程师交流群

    评论

    相关推荐
    热点推荐

    和栈的区别

    一个由C/C 编译的程序占用的内存分为以下几个部分: 栈区(stack):由编译器自动分配释放 ,存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。 区(heap):一般由
    的头像 发表于 11-27 18:13 898次阅读

    国密系列算法简介及SM4算法原理介绍

    一、 国密系列算法简介 国家商用密码算法(简称国密/商密算法),是由我国国家密码管理局制定并公布的密码算法标准。其分类1所示: 1
    发表于 10-24 08:25

    搞懂LDO的硬件设计和应用

    存在误解,这些误解往往会导致设计失败。  ” 我们先通过一来概括 LDO 的应用: 线性稳压电源与 LDO 先澄清一个基础的概念 : LDO 是线性稳压电源的一种 ,但不是所有线性稳压电源都是 LDO。两者都是 通过 反馈控制环路 来实现稳压的
    的头像 发表于 09-23 17:22 7933次阅读
    一<b class='flag-5'>张</b><b class='flag-5'>图</b>搞懂LDO的硬件设计和应用

    能源数字化转型核心!一揭秘绿电直连“流合一”超级架构

    本文将化繁为简,通过“源、网、荷、储、碳”大核心要素,为您全景解析其系统架构,揭秘如何实现“流合一”的智慧协同。
    的头像 发表于 09-17 17:07 471次阅读
    能源数字化转型核心!一<b class='flag-5'>张</b><b class='flag-5'>图</b>揭秘绿电直连“<b class='flag-5'>五</b>流合一”超级架构

    阿童木双检测器:金家电冲压叠料检测方案

    一、方案简介当前金家电行业(如洗衣机内筒、冰箱门板、空调外机支架等冲压件生产)面临“双叠料导致模具损耗大、传统检测适配性差、多品种换产效率低”三大核心问题,直接影响生产效益与产品质量。本方案
    的头像 发表于 08-22 15:44 593次阅读
    阿童木双<b class='flag-5'>张</b>检测器:<b class='flag-5'>五</b>金家电冲压叠料检测方案

    金冲压行业双检测案例:阿童木 MDSC-900E 的智能识别与高速响应应用

    一、项目背景某金制造企业主营金属零部件生产,核心工序为铁片冲压成型。在冲压前,需将0.8mm厚的铁片通过送料机送入冲压设备。此前因缺乏可靠的双检测手段,常出现铁片重叠(双或多张)送料的情况
    的头像 发表于 06-17 16:16 419次阅读
    <b class='flag-5'>五</b>金冲压行业双<b class='flag-5'>张</b>检测案例:阿童木 MDSC-900E 的智能识别与高速响应应用

    阿童木通过式金属双检测器:高速精准,重塑生产效能

    在金属加工领域,双叠料检测是保障产线稳定运行的关键环节。阿童木通过式金属双检测器以高速响应、宽域检测、抗干扰强、智能互联四大核心优势,成为金家电、锂电叠片、金属包装等行业的“效率守护者”。一
    的头像 发表于 05-22 11:22 717次阅读
    阿童木通过式金属双<b class='flag-5'>张</b>检测器:高速精准,重塑生产效能

    高压放大器在压电叠主动隔振实验中的应用

    作为传感器件,并搭建了主动隔振系统试验平台;(2)分别对钢梁及平板结构进行模态测试试验,验证有限元模型的真实性及可靠性;(3)MCS控制算法作为主动隔振系统的控制策略,进行压电叠主动隔振试验研究,结合数据处理软件
    的头像 发表于 04-10 11:16 543次阅读
    高压放大器在压电叠<b class='flag-5'>堆</b>主动隔振实验中的应用

    :整流电路的“中流砥柱”

    大家好!今天我们来聊一聊电子电路中一个非常重要的元器件——桥。无论是家用电器、工业设备,还是通信设备,桥都扮演着不可或缺的角色。它虽然看起来不起眼,但却是整流电路的“中流砥柱”。那么,桥到底是
    的头像 发表于 04-01 17:07 2122次阅读

    一周带你看懂电路

    教你看懂电路 电源电路单元 一电路通常有几十乃至几百个元器件,它们的连线纵横交叉,形式变化多端,初 学者往往不知道该从什么地方开始, 怎样才能读懂它。其实电子电路本身有很强的规律性, 不管多
    发表于 03-03 15:05

    用DLP4500 exe软件来对248bit的合成824bit的后,最后设置完sequence,play的时候就只显示了8,为什么?

    Sequence Settting 设置时也是将248bit按顺序加入到Pattern Sequence中,不知道为啥。
    发表于 02-25 07:52

    第四代核电型:钠冷快设计的流体仿真技术挑战与解决方案

    第四代核反应是目前核能技术发展的前沿方向,具有更高的安全性、经济性、可持续性以及防扩散能力等特点。
    的头像 发表于 02-24 11:17 1708次阅读
    第四代核电<b class='flag-5'>堆</b>型:钠冷快<b class='flag-5'>堆</b>设计的流体仿真技术挑战与解决方案

    用DLP4500烧录98bit位深度的相移,3合成了一24bit,结果每一24bit都重复投射三次,这是为什么?

    你好,吴工,用DLP4500烧录98bit位深度的相移,3合成了一24bit,结果每一24bit都重复投射三次,想问下这是为什么
    发表于 02-24 08:00

    取料机DCS数据采集物联网解决方案

    在火电厂中,取料机承担着储存、输送原料的重要任务,通过PLC、DCS等执行允许料、允许取料、外围取料运行、取料机紧急停机、料运行、
    的头像 发表于 12-24 10:23 826次阅读
    <b class='flag-5'>堆</b>取料机DCS数据采集物联网解决方案

    求助,ISO7721用于485隔离遇到的疑问求解

    是mA级的,所以是不是需要在输出脚串一个K级的电组来限流? ②我查阅了TI所有跟ISO7X21的参考设计原理,其中没有任何一原理串联了上面描述的限流电阻,为什么?
    发表于 12-18 06:30