heap

如何手写一个堆?

堆对外提供哪些 api ?

① 插入一个元素

② 求集合中的最小值

③ 删除最小值

延伸操作:

④ 删除任意一个元素

⑤ 修改任意一个元素

堆的基本结构

堆是一个完全二叉树

完全二叉树: 除了最后一层, 其它层全是非空节点. 最后一层的节点按照从左往右排列, 中间没有空节点

小根堆的性质: 每一个节点都是小于等于左右儿子节点

堆的存储方式

一维数组

完全二叉树都是用一维数组存的

堆的基本操作

一维数组 heap[] 和 数组长度 int size 来完成下面的操作

数组存储所有元素, size 表示有效元素的长度

down 操作

down(x) 将指定元素下沉

当我们修改一个元素后进行 down 操作, 过程如下图

image-20240219130434771

up 操作

up(x) 将指定元素上升

如何从一个一维数组建堆

如果是每次插入一个元素就开始 down, 那么时间复杂度是 NlogN

从 $\frac{n}{2}$ 开始往树根 down 元素, 可以达到 O(N)的时间复杂度

如下图, 对于树中到最后一层节点, 我们是不需要进行 down 操作的, 因为 他们没有儿子节点, down 了也没用, 所以我们从 $\frac{n}{2}$ 开始往树根 down 元素, 也就是圈起来的那一层. 对于这一层, 有 n/4 个元素, 他们需要往下 down 1次, 在往上一层有 $\frac{1}{2} * \frac{n}{4} = \frac{n}{8}$ 个元素, 他们需要往下 down 两次, 以此类推

证明过程

新增元素

直接作为一个新的节点插入到树上. 然后 up 这个节点, 是否能 up 成功, 我们会在 up 操作中判断

修改元素

修改指定元素, 然后无脑执行 up 和 down 操作, 由于 up 操作和 down 操作会自动判断能否 up 或者 down, 所以肯定只有一个操作能成功, 就不需要再修改元素之后考虑应该 up 还是 down 了

删除元素

让最后一个元素覆盖掉指定的元素, 然后 size–. 并且对覆盖后的元素进行 down 操作

取堆顶元素—求集合当中的最小值