「这是我参与2022首次更文挑战的第3天,活动详情查看:2022首次更文挑战」。
- 前情回顾,完全二叉树的
思维逻辑结构
与实际的程序存储结构
- 下面左图为完全二叉树的逻辑结构,右图为其在程序中的实际存储结构
什么是堆
- 可以理解为是基于完全二叉树的一种数据结构
- 大顶堆
- 在二叉树中任意有关联的三个节点,都满足
父节点的值
都大于两个子节点的值
- 在二叉树中任意有关联的三个节点,都满足
- 小顶堆
- 在二叉树中任意有关联的三个节点,都满足
父节点的值
都小于两个字节点的值
- 在二叉树中任意有关联的三个节点,都满足
堆的尾部插入调整
- 以
大顶堆
为例 - 由于堆就是完全二叉树,所以当有新增元素时,需要将新增元素放到完全二叉树的最后一层的最左侧
- 对应的,也就是在实际程序的连续存储区域的尾部,插入新增的元素
- 插入之后需要进行向上调整,使得整棵树再次满足
大顶堆
的性质,调整过程如下:- 子节点 6 大于父节点 3,所以要将 6 和 3 调换位置
- 在父节点 5 与两个子节点 4,6 中,6 最大,所以将 6 与 5 调换位置
- 最后,整颗树满足 大顶堆 的性质,停止调整
堆的头部弹出调整
- 以上面插入元素后的
大顶堆
为例 - 当弹出堆顶元素后,也就是对应的连续存储空间的头部元素被弹出
- 为了维护堆是完全二叉树的性质,通常需要将完全二叉树的,最底层的最右边的元素,放至堆顶
- 也就是将对应连续存储空间的尾部元素,放至头部,使得现在的连续存储空间的前 n - 1 位都是连续的存储着元素
- 然后再次为了维护 大顶堆 的性质,需要将堆顶元素进行向下调整,调整过程如下:
- 在父节点 3 和两个子节点 4、5中,5 最大,所以只需要将 3 和 5 交换位置
- 最后,整颗树满足 大顶堆 的性质,停止调整
堆排序
- 步骤
- 将原始元素进行建堆操作
- 将堆顶元素与堆尾元素调换位置,将该操作视为堆顶元素的弹出操作
- 头部元素弹出之后,按照堆的性质,调整剩余的堆元素,也就是连续存储空间中的前 count - 1 个元素
- 重复上述调换操作,直到堆的合法元素个数为 0,此时此前所有被弹出的元素组成的序列是有序的
- 每次调换位置,使得堆顶部元素不属于堆的合法位置,但是该元素依然存在于这片连续存储区
- 所以经过 n 次调换,当前连续存储区的所有元素是有序排列的
优先队列
- 通常情况下,
优先队列
是用堆
来实现的,即堆
是优先队列
的一种实现方式 优先队列
实际上也是,我们变换了思维方式后,对堆
的一种叫法- 堆 的两个操作
弹出堆顶元素
和尾部插入元素
,与队列的两个操作出队头部元素
和尾部入队元素
是极其相似的 - 但是不同的是,堆每次弹出的元素,都是当前所有元素中的
最值
- 如果将
最值
看做成优先级
,则每次弹出的元素都是当前所有元素中优先级最高
的元素 - 所以可以将
堆
实际对应的连续存储的序列
,称为优先队列
普通队列 | 优先队列 |
---|---|
尾部入队 | 尾部可以插入 |
头部出队 | 头部可以弹出 |
先进先出 | 每次出队,队列中的最(大/小)值 |
可以用数组实现 | 可以用数组实现,思维逻辑上看作成一个堆 |
堆适合维护集合最值
最后
- 堆的分享就到这里了,欢迎大家在评论区里面讨论自己对堆的理解 👏。
- 如果觉得文章写的不错的话,希望大家不要吝惜点赞,大家的鼓励是我分享的最大动力🥰