2020-05-08  本文已影响0人  Robin92

堆简述

堆(heap)的结构是一个完全二叉树的结构。

堆分大根堆和小根堆。如果一个二叉树它即不是大根堆,也不是小根堆,那它不是堆。

大根堆:每一个根节点,都大于它的两个子节点。
小根堆:每一个根节点,都小于它的两个子节点。

注意,大根堆和小根堆并不能保证在不同分支上它们的大小关系有规律。

树的高度为 logN(可以发现每一层満时它在此层 i 的元素个数为 2^i)。

完全二叉树,每个节点最多有两节点,完全二叉树中,每一层或是満的,或是从左到右依次变満的状态。

参考:https://visualgo.net/en/heap

堆的实现

堆可以用数组实现。

[0, 1, 2, 3, 4, 5] 可依次从上到下,从左到右填入完全二叉树中。

数组放入完全二叉树

则数组下标中 i 位置,它寻找节点的关系为

有时,常常为了方便,将数组下标 0 的元素弃而不,所以当 i' 从 1 开始时,则对应关系为:

不用 0 元素,是因为在找下标时很容易用移位(>> 或 <<)的方式找到关联的下标位置。
而对 0 移位操作得到的结果始终为 0,不方便找寻相关节点的下标。
2 * i + 1 = i << 1 | 1

维护大根堆

小根堆与大根堆规则一样,不再缀述。

请参考 https://visualgo.net/en/heap 中的演示。

添加过程

所以,构建大根堆的过程:

因为添加每一个元素它移动的步数正好为二叉树的高度,所以每一步的复杂度 logN。

移除堆中的最大值后调整以维持大根堆(heapify)

方法:将最后一个值放到移除的位置(原来最大值的位置),让最小值向下沉,直到找到合适位置。

步骤:

堆排序

堆排序是指,给定一个数组,然后将此数组构建成堆(这里为大根堆),然后将它排序成从小到大的顺序。

方法一

有了上方维护大根堆过程的原理后,这个方法就很好理解了:

此时整个数组就变成了已排序的了。

时间复杂度 O(N*logN) + O(logN) * O(N) => O(N*logN)

方法二

在方法一上进行优化。优化点是在数组构建成大根堆的过程上(也就是方法一的步骤 1 中)。

数组构建为大根堆优化版思路总述:将数组看成一个完全二叉树,从下往上开始,保证每一个子树为大根堆(小值下沉的方法),最后得到一个大根堆。

步骤:

这其中,不用判断最后一层,只需要用下标从 N-1 变到 0 即可。

这种方法将构建大根堆的时间复杂度优化为 O(N)。

小结:构建堆,自上而下的方法时间复杂度为 O(N*logN),自下而上的方法时间复杂度为 O(N)。

面试题:几乎有序的数组的排序

几乎有序是指,若将数组排好顺序,每个元素移动的位置不超过一个常数 k。

思路,这个题就可以用堆(具体是小根堆)来做。根据题意,要得到的排序结果在 i 位置上的数一定是在 i - k 到 i + k 之间,所以有 2k + 1 个可能。但从第一个元素开始放入正确的值时,后面每一个位置的可选项只有 k + 1 个。所以堆的大小是 k + 1,每 k + 1 个数放满小根堆后就可以确定第一个小值所在的位置。堆不满而元素已放完时,从剩下堆中选即可。

复杂度,O(N*logk)

上一篇下一篇

猜你喜欢

热点阅读