堆(Heap)可以看成近似完全二叉树的数组,树中每个节点对应数组中一个元素。除了最底层之外,该树是完全充满的,最底层是从左到右填充的。
堆包括最大堆和最小堆:最大堆的每一个节点(除了根结点)的值不大于其父节点;最小堆的每一个节点(除了根结点)的值不小于其父节点。
本文以最大堆为例,先直观感受一下堆的样子:
图片来源
最大堆
树中节点内的值表示存储的值,节点旁边的值表示该节点在数组中的下标。
观察上图不难发现,对于一个给定节点的下标i,其父节点、左孩子、右孩子的下标为:
parent(i): (i + 1) // 2 - 1
left(i): i * 2 + 1
right(i): i * 2 + 2
其中“//”表示除法后只取整数部分。
对于一个给定的数组,如何转化为堆呢?
对于一个任意节点,如果该节点大于左孩子和右孩子,则该节点无需移动。否则,和左右孩子中较大的那个交换位置,于是该节点满足大于左右孩子了,但是刚被交换了的孩子节点需要继续比较它的左右孩子......整个过程递归进行下去,我们可以轻松计算出:对于根节点,在进行转化成堆的过程中,最多需要交换h次(h为树的高度),对于树种的每个节点,如果都进行此操作,则整个过程的时间复杂度为:O(nlogn)。(n为节点数,logn≈h)