Java数据结构与算法(树)——堆(完全二叉树)


注意:这里的堆和 Java,C++ 等编程语言在内存中的“堆”是不一样的,这里的堆是一种树结构。

一、堆的定义

(1)它是完全二叉树

完全二叉树——对于一棵具有 n 个节点的二叉树按层序编号,如果编号为 i 的节点与同样深度的满二叉树中编号为 i 的节点在二叉树中的位置完全相同,则这棵树称为完全二叉树。

(2)通常用数组来实现

用数组实现二叉树。假设某个节点的索引为 index,则

  • 该节点的左子节点的索引是 (2×index+1)(2 \times index + 1)
  • 该节点的右子节点的索引是 (2×index+2)(2 \times index + 2)
  • 该节点的父节点的索引是 (index1)÷2(index - 1) \div 2

(3)堆中每个节点的关键字都大于其子节点的关键字

注意堆与二叉搜索树的区别:

  • 对于二叉搜索树,每个节点的关键字都大于其子节点的关键字,并且,每个节点的左子节点的关键字都小于右子节点的关键字,通过中序遍历就可实现升序遍历;
  • 对于堆,只要求每个节点的关键字都大于其子节点的关键字,而每个节点的左、右子节点没有确定的大小关系。只有沿着从根节点到叶节点的每一条路径上是降序排列的。因此,相比于二叉搜索树,堆是弱序的。

二、堆的主要操作

优先级队列提供了删除最大(或最小)数据项和插入数据项的方法,可以用有序数组来实现。这种实现方式删除操作的时间复杂度为 O(1),但是插入操作的时间复杂度还是O(N),因为每次插入平均需要移动一半的数据项,来保证插入后的数组依然有序。

用堆实现优先级队列时,其插入和删除的时间复杂度都为 O(logN),删除的时间变慢,但是提高了插入的时间。所以当有很多插入操作时,可以选择用堆来实现优先级队列。

1、遍历和查找

由于堆是弱序的,所以在遍历和查找的过程中,没有足够的信息来确定选择两个子节点的哪一个来走向下一层,因此,堆是不支持查找和遍历的。堆的主要作用就是快速删除最大(最小)节点和快速插入数据。

2、删除

堆的删除操作是指删除关键字最大的节点,即根节点。根节点移除后,还需将这个空的节点填上。

两种方法:

(1)所有数据项向前移动一个单元,但很费时;
(2)把最后一个节点放到根节点的位置,再向下筛选,使得该节点位于一个大于它的节点之下,小于它的节点之上(向下筛选时,将目标节点和其子节点比较,哪个子节点大就和哪个交换位置)。过程图示如下:
Java数据结构与算法(树)——堆(完全二叉树)

3、插入

插入节点时,先将节点插入数组最后一个数据的后一个位置,然后向上筛选,将该节点放在合适的位置(向上筛选,和其父节点比较,放到比其父节点小的位置即可)。

三、代码实现