1 堆就是用数组实现的二叉树,所有它没有使用父指针或者子指针。堆根据“堆属性”来排序,“堆属性”决定了树中节点的位置。
堆属性:
堆分为两种:最大堆和最小堆,两者的差别在于节点的排序方式。
在最大堆中,父节点的值比每一个子节点的值都要大。在最小堆中,父节点的值比每一个子节点的值都要小。这就是所谓的“堆属性”,并且这个属性对堆中的每一个节点都成立。
一个堆中的节点的高度为该节点到根节点最长简单路径上边的数目;
包含n个元素的堆的高度为θ(lgn);
PARENT(i) return Math.floor(i/2):求下标为i的元素的父节点的下标;
LEFT(i) return 2i:求下标为i元素的左子树的节点的下标;
RIGHT(i) return 2i+1:求下标为i元素的右子树的节点的下标;
2 含n个元素的堆的高度为floor(lgn)
假设n个元素的堆的高度为h。得2^h <= n <= 2^(h+1) - 1,因此h <= lgn < h+1。
3 当i>A.heap-size/2时,调用MAX-HEAPIFY(A,i)会有什么结果?
那么i都是叶子结点,所以原最大堆也不会改变。
对于一个树高为n的结点来说,MAX-HEAPIFY的时间复杂度是O(h)
MAX-HEAPIFY(A,i)
while (i<=A.heap-size/2)//由于6.2-4知道,i>A.heap-size/2以后,最大堆不会有任何改变。
{ //这样也可以减少循环次数。
l=LEFT(i)
r=RIGHT(i)
if l<=A.heap-size and A[l]>A[i]
largest=l
else largest=i
if r<=A.heap-size and A[r]>A[largest]
largest=r
if largest!=i
exchange A[i]<->A[largest]
i=largest//把largest赋值给i,然后继续进行MAX-HEAPIFY(A,i)循环。
}
4 建堆方案:
总的时间复杂度为O(nlgn),但这个上界不是渐进紧确的,O(n)
由堆的性质可知,子数组A([n/2]+1..n)中元素都是树的叶节点。每个叶节点都可以看成只包含一个元素的堆;
BUILD-MAX-HEAP(A){
A.heap-size=A.length;
For I = [A.length/2] downto 1
MAX-HEAPIFY(A,i);
}
5 堆排序:
时间复杂度为O(nlgn)
以最大堆为例,每次将第一个元素与最后一个元素交换,同时将堆的大小减一
HEAPSORT(A)
BUILD-MAX-HEAP(A)
for i = A.length downto 2
exchange A[1] with A[i]
A.head-size = A.head-size - 1
MAX-HEAPIFY(A,1)
6 优先队列:
在一个包含n个元素的堆中,所有的优先队列的操作都可以在O(lgn)时间内完成
插入(INSERT(S,x))
取得最大值(MAXIMUM(S))
删除并返回最大值(EXTRACT-MAX(S))
将某一个元素key增大到制定的值(INCREASE-KEY(S,x,k))
HEAP-EXTRACT-MAX(S)
if A.heap-size < 1
error "heap underflow"
max = A[1]
A[1] = A[A.heap-size] # 将最后一个元素赋值给A[1],等同于删除最大值
A.heap-size = A.heap-size - 1 # 通过减少堆数组的长度来‘删除’最后一个元素
MAX-HEAPIFY(A,1) # 将A[1]中的元素放到恰当的位置
return max
INSERT(S,x)
HEAP-INSERT(S,key)
A.heap-size = A.heap-size - 1
A[A.heap-size] = -∞
HEAP-INCREASE-KEY(A,A.heap-size,key)