堆是一种树,而不是Java和C++等提及的内存中的“堆”
前面章节提到的优先级队列可以用于计算机中的任务调度,在计算机中有些程序和活动需要比其他的程序和活动先执行,因此要分配更高的优先级
优先级队列是一种抽象数据类型,提供了删除最大或最小关键字值的数据项的方法,以及插入数据项的方法等
在第四章中优先级队列是用有序数组的形式实现的,这种方法的问题是尽管删除的时间复杂度为O(1),但是插入的时间复杂度仍是较长的O(N),因此必须要移动数字中平均一般的数据项以插入新的数据
由堆实现的优先级队列的插入和删除的时间复杂度都是O(logN),尽管删除的时间慢了一点,但是插入的时间快多了
堆的介绍
堆是一种特殊的二叉树,它的特点如下
堆是完全二叉树。也就是说,除了树的最后一层节点不需要是满的,其他的每一层从左到右都完全是满的
堆常常用一个数组实现
堆中的每一个节点都满足堆的条件,也就是说每一个节点的关键字都大于或等于这个节点的子节点关键字
下图展示了堆与实现他的数组之间的关系。
堆在存储器中表示的数组,堆只是一个概念上的表示,需要注意的是,树是完全二叉树,并且所有的节点都满足堆的条件
弱序
堆和二叉搜索树相比是弱序的
在二叉搜索树中所有节点的左子孙的关键字都小于右子孙的关键字,也就是说,二叉搜索树通过一个简单的算法就可以按序遍历节点
而在堆中按序遍历节点是很困难的,因为堆的组织规则比二叉搜索树的组织规则要弱。对于对来说,只要求沿着根到叶子的每一条路径,节点都是按降序排列的
由于堆是弱序的,所以一些操作是困难的或者是不可能的。除了不支持便利以外,也不能在堆上便利的查找指定关键字。因为在查找过程中,没有足够的信息来决定选择通过节点的两个子节点中的哪一个走向下一层,他也不能在少至O(logN)的时间内删除一个指定关键字的节点,因为没有办法能够找到这个节点。
因此,堆的这种组织似乎非常接近无需。不过,对于快速一处最大节点的操作以及快速插入新节点的操作,这种顺序已经足够了。
移除
移除是指删除关键字最大的节点。这个节点总是根节点,所以一处是很容易的,跟在对数组中的索引总是0
然而移除根后这棵树就不再是完全的了,数组中on个就有了一个空的数据单元,移除最大节点的步骤如下
1.移走根
2.把最后一个节点移动到根
3.一直向下筛选这个节点,知道他在一个大于他的节点之下,小于他的节点之上为止
如图所示
注意向下筛选的算法要检查哪一个子节点更大,然后目标节点和较大的子节点交换位置
插入
插入节点也是很容易的。插入使用向上筛选,而不是向下筛选。
节点初始时插入到数组最后一个空着的单元中,数组容量大小增一
插入向上筛选新节点,知道他在一个大于他的节点之下,在一个小于它的节点之下
如图
扩展堆数组
在Java中使用Vector类对象取代数组对象,Vector对象可以动态扩展
堆操作的效率
二叉树的层数L等于log2(N+1)
向下或向上筛选的循环次数为L-1
所以执行时间和log2N成正比
综上,讨论的堆操作的时间复杂度都是O(logN)