堆是什么?
堆是一种满足以下条件的树:
堆中的每一个节点值都大于等于(或小于等于)子树中所有节点的值。称为大顶堆(小顶堆)
(注意:堆不一定是完全二叉树,只是为了方便存储和索引,我们通常用完全二叉树的形式来表示堆)
堆有什么用?
当我们只关心所有数据中的最大值或者最小值,存在多次获取最大值或者最小值,多次插入或删除数据时,就可以使用堆。
有小伙伴可能会想到用有序数组,初始化一个有序数组时间复杂度是 O(nlog(n))
,查找最大值或者最小值时间复杂度都是 O(1)
,但是,涉及到更新(插入或删除)数据时,时间复杂度为 O(n)。
相对于有序数组而言,堆的主要优势在于更新数据效率较高。 堆的初始化时间复杂度为 O(nlog(n))
,堆可以做到O(1)
时间复杂度取出最大值或者最小值,O(log(n))
时间复杂度插入或者删除数据。
堆如何存储?
之前介绍树的时候说过,由于完全二叉树的优秀性质,利用数组存储二叉树即节省空间,又方便索引(若根结点的序号为1,那么对于树中任意节点i,其左子节点序号为 2\*i
,右子节点序号为 2\*i+1
)。
堆的操作
堆的操作主要包括两种 : 插入元素 和 删除堆顶元素。
插入元素:
1.将要插入的元素放到最后
2.从底向上,如果父结点比该元素小,则该节点和父结点交换,直到无法交换
删除堆顶元素:
堆顶元素总是最大的(or 最小的)。所以当我们需要多次查找最大元素或者最小元素的时候,可以利用堆来实现。删除堆顶元素后,为了保持堆的性质,需要对堆的结构进行调整,我们将这个过程称之为"堆化",堆化的方法分为两种:
- 一种是自底向上的堆化:首先删除堆顶元素,此位置空出。比较空出节点的左子节点和右子节点,将较大的元素填充到空位。一直循环比较空出位置的左右子节点,并将较大者移至空位。但是,我们会发现数组中出现了“气泡”,这会导致存储空间的浪费。
- 另一种是自顶向下堆化:我们将最后一个元素移动到堆顶。不停与左右子节点的值进行比较,和较大的子节点交换位置,直到无法交换位置。
堆排序
分为两步:
- 第一步是建堆,将一个无序的数组建立为一个堆
- 第二步是排序,将堆顶元素取出,然后对剩下的元素进行堆化,反复迭代,直到所有元素被取出为止。