一、堆的概念
介绍堆之前,首先来看二叉树 Binary Tree。
二叉树是树的一种,主要的特点是二叉树的所有节点最多只有两个叶节点。除此之外没有别的要求。
- Complete Binary Tree(完全二叉树): 二叉树的一种。在完全二叉树当中,除了最后一层之外,所有层的节点都是满的,且最后一层的节点也是从左到右的。优先填满左边的节点。
- Full Binary Tree(满二叉树): 二叉树的一种。满二叉树的所有层,包括最后一层,都是满的。也就是说,除了最后一层的节点外所有的节点都有两个子节点。
这两者之间的关系:完全二叉树是从满二叉树里引出的。满二叉树最下一层的子节点,如果是从右往左拿掉,不论多少,剩下的都是完全二叉树,如果不是从右往左拿,而是在中间拿掉了一个,就不是完全二叉树了。
满二叉树
和完全二叉树
如下图示例:
很容易理解吧!
然后再看 堆 Heap 的定义。
Heap(堆): 堆是一种完全二叉树。在树的性质之外,堆要求节点按照大小(父节点比子节点大/父节点比子节点小)来排列。除完全二叉树的性质外,他还要求堆内元素按照某种固定的大小顺序排列。
- Min Heap(最小堆): 最小的键值总是在最前面。换句话说,所有的父节点都比他们的子节点小。
- Max Heap(最大堆): 最大的键值总是在最前面。换句话说,所有的父节点都比他们的子节点大。
最小堆也叫小顶堆,小根堆;最大堆也叫大顶堆,大根堆。
那么我们填入数字,对最小堆、最大堆,做一个示例:
二、堆的存储和堆排序思想
2.1 堆存储
堆在逻辑上就是树,因此存储的方式也可以是链表,不过按照这种存储,需要数据本身,以及节点之间的关系指针,耗费空间比较大。
由于堆是完全二叉树,除了最下面的一层,其余层都是满的,那么每层的节点个数就一定是 1、2、4、8、……、2^n-1-x(最后一层可满可不满)
他们之间的下标关系很明确,因此一般用数组存储,访问起来也很方便。
下图是对于上面示例的小顶堆和大顶堆的对应数组存储示意。
可以看到,对于小顶堆
的性质,可以直接用数组之间的下标关系表示成为:
arr[ i ] <= arr[ 2 i + 1 ] && arr[ i ] <= arr[ 2i + 2 ]
同理,大顶堆
:
arr[ i ] >= arr[ 2i + 1 ] && arr[ i ] >= arr[ 2i + 2 ]
也就是任意一个子树的父节点都小于等于(大于等于)他们的左右子节点,前提是存在。
可以看到,堆的性质本身带有递归的特性:
对于根节点来说,子树的节点大小都大于(小于)他;
对于左右子树,同样有他们的子树的节点大小都大于(小于)他;
……
这种大小关系很明确的数据结构的应用,最经常就是堆排序。
2.2 堆排序
堆排序的思想,以构造大顶堆为例:
- 将长度为 n 的待排序序列构造成一个大顶堆,此时,整个序列的最大值就是堆顶的根节点。
- 将其与末尾元素进行交换,此时末尾就为最大值。
- 然后将剩余 n-1 个元素重新构造成一个大顶堆,这样会得到 n 个元素的次小值,如此反复执行,便能得到一个有序序列了。
说白了,就是依次构建大顶堆,这样就能拿出一个一个的当前最大值,最后得到有序序列。
这个过程具体实现起来有一些问题需要解决。
1.输入无序