要点总结
- 堆的两个要素:是
完全二叉树
并且父节点的值一定大于(大根堆)或小于(小根堆)它的左右孩子
。所有的操作,仔细思考后会发现,都是为绕这两点在做功课。
- 构建时使用的基础数据结构,一般使用数组模拟完全二叉树,模拟方法:
1号元素即为根节点
对于一个编号为n的节点,其左子节点编号为
2n
,其右子节点编号为2n + 1
,其父节点编号为n // 2
如果总结点数为
n
,则序号大于n/2
的节点都是叶子节点
要取最后一个叶子节点,直接取数组末尾元素即可
- 向堆中插入元素
先直接将元素插入到数组末尾(即完全二叉树的)
然后将其进行
上浮
操作,如果是小根堆,操作为:如果节点小于其父节点,则与父节点交换位置,此时节点位置将会上升(所以称为上浮)。重复操作至无法上浮
。大根堆同理。
- 弹出堆顶元素
先直接移动最后一个元素去覆盖第一个元素
然后对此时的顶部元素进行
下沉
操作,如果是小根堆,操作为:如果当前节点没有比两个孩子节点都小,则将当前节点与左右孩子中的较小者交换(下沉
)。重复操作至不能下沉
- 相关时间复杂度
单纯地取最小值,或者输出size。时间O(1),空间O(1)
插入删除操作。时间O(logn),空间O(1)
构造堆时有两种思路:如果是创建空堆并且一个一个插入,则时间复杂度为O(nlogn);但是其实可以优化,进行一个称之为
堆化
的操作,先将乱序的数组直接创建完全二叉树,然后自底向上,一层一层地进行类似下沉
操作。时间复杂度:时间复杂度为O(n),详解如下
建堆详细时间复杂度证明
设整个堆的节点个数为 n a c t u a l n_{actual} nactual,
在使用siftdown建堆时,鉴于最底层的节点时不需要操作的,我们将其剔除
所以需要操作的节点数为 2 k − 1 2^k-1 2k−1, 其中 k = f l o o r ( l o g 2 n a c t u a l ) k = floor(log_2n_{actual}) k=floor(log2nactual),为了便于计算,我们令 n = 2 k n = 2^k n=2k
这样一来,我们要计算的部分变得十分规整,其中 n 2 n\over2 2n个节点最多向下操作一次, n 4 n\over4 4n个节点最多向下操作两次,依次类推
求和式可写为: S = 1 × n 2 + 2 × n 4 + 3 × n 8 + . . . + ( k − 1 ) × n 2 k − 1 + k × n 2 k S = 1 \times {n\over2} + 2 \times {n\over4} + 3 \times {n\over8} + ... + (k-1) \times {n\over2^{k-1}} + k \times {n\over2^k} S=1×2n+2×