1.什么是堆?
定义:
堆通常是可以看作一棵树的数组
对象,堆是非线性数据结构,相当于一维数组。堆总是满足下列性质:
- 堆中某个节点的值总是不大于或不小于其父节点的值;
- 堆总是一棵完全二叉树。
将根节点最大的堆叫做最大堆或大根堆,根节点最小的堆叫做最小堆或小根堆。
下面我们将会学习:
- 建初堆:如何将一个无序序列建成一个堆?
- 调整堆:去掉堆顶元素,在堆顶元素改变之后,如何调整剩余元素成为一个新的堆?
因为建初堆要用到调整堆的操作,所以下面先讨论调整堆的实现。
2.调整堆
先看一个例子,如图a是一个堆,将堆顶元素97和堆中最后一个元素38交换后,如图b所示。由于此时除根节点外,其余节点均满足堆的性质,由此仅需自上至下进行一条路径上的结点调整即可。首先以堆顶元素38和其左、右子树根结点的值进行比较,由于左子树根节点的值76大于右子树根节点的值65,则将堆顶元素38与76交换;由于38替换了76之后,破坏了左子树的“堆”,则需进行和上述相同的调整直到叶子节点,那么此时左子树就满足了堆的性质,调整后的状态如图c所示。重复上述过程,将堆顶元素76和堆中最后一个元素27交换,此时右子树是被破坏的“堆”,则向上面一样进行调整,最后得到新堆。
上述过程就像过筛子一样,把较小的关键字逐层筛选下去,而较大的关键字逐层选上来
。因此,称此为“筛选法”。
假设r[s+1] ~ r[m]已经是堆的情况下,按“筛选法”将r[s] ~ r[m]调整为以r[s]为根的堆,算法实现如下:
算法步骤:
从r[2s]和r[2s+1]中选出关键字较大者,假设r[2s]的关键字较大
,比较r[s]和r[2s]的关键字。
- 若r[s].key>=r[2s].key,说明以r[s]为根的字数已经是堆,不必做任何调整。
- 若r[s].key<r[2s].key,交换r[s]和r[2s]。交换后,以r[2s+1]为根的子树仍为堆,如果以r[2s]为根的子树不是堆,则重复上述过程,将以r[2s]为根的子树调整为堆,直至进行到叶子结点为止。
伪代码:
void HeapAdjust