计算机程序的思维逻辑 (45) - 神奇的堆
前面几节介绍了Java中的基本容器类,每个容器类背后都有一种数据结构,ArrayList是动态数组,LinkedList是链表,HashMap/HashSet是哈希表,TreeMap/TreeSet是红黑树,本节介绍另一种数据结构 - 堆。
引入堆
之前我们提到过堆,那里,堆指的是内存中的区域,保存动态分配的对象,与栈相对应。这里的堆是一种数据结构,与内存区域和分配无关。
堆是什么结构呢?这个我们待会再细看。我们先来说明,堆有什么用?为什么要介绍它?
堆可以非常高效方便的解决很多问题,比如说:
- 优先级队列,我们之前介绍的队列实现类LinkedList是按添加顺序排队的,但现实中,经常需要按优先级来,每次都应该处理当前队列中优先级最高的,高优先级的,即使来得晚,也应该被优先处理。
- 求前K个最大的元素,元素个数不确定,数据量可能很大,甚至源源不断到来,但需要知道到目前为止的最大的前K个元素。这个问题的变体有:求前K个最小的元素,求第K个最大的,求第K个最小的。
- 求中值元素,中值不是平均值,而是排序后中间那个元素的值,同样,数据量可能很大,甚至源源不断到来。
堆还可以实现排序,称之为堆排序,不过有比它更好的排序算法,所以,我们就不介绍其在排序中的应用了。
Java容器中有一个类PriorityQueue,就表示优先级队列,它实现了堆,下节我们会详细介绍。关于后面两个问题,它们是如何使用堆高效解决的,我们会在接下来的几节中用代码实现并详细解释。
说了这么多好处,堆到底是什么呢?
堆的概念
完全二叉树
堆首先是一颗二叉树,但它是完全二叉树。什么是完全二叉树呢?我们先来看另一个相似的概念,满二叉树。
满二叉树是指,除了最后一层外,每个节点都有两个孩子,而最后一层都是叶子节点,都没有孩子。比如,下图两个二叉树都是满二叉树。

满二叉树一定是完全二叉树,但完全二叉树不要求最后一层是满的,但如果不满,则要求所有节点必须集中在最左边,从左到右是连续的,中间不能有空的。比如说,下面几个二叉树都是完全二叉树:

而下面的这几个则都不是完全二叉树:
编号与数组存储
在完全二叉树中,可以给每个节点一个编号,编号从1开始连续递增,从上到

