堆分为大根堆与小根堆,这里以大根堆为例。PS:这里的堆只涉及二叉堆,斐波那契堆什么的。。智商不够并不能学会- - !
定义:
二叉堆通常是一个用数组实现的完全二叉树。并且大根堆满足对于任何一颗子树,其孩子节点的key总是不会比根节点的大。所以堆顶元素(即树根)就是key最大的元素。
堆应该支持的操作:
(1)MAX-HEAPIFY //从上往下调整堆,用于建立堆
(2)BUILD-MAX-HEAP //建立一个大根堆
(3)HEAPSORT //将大根堆中的数据按照关键字升序排序
(4)MAX-HEAP-INSERT、HEAP-EXTRACT-MAX、HEAP-INCREASE-KEY和HEAP-MAXIMUN //用于建立优先队列,意义依次为插入新元素,
//返回并删除堆顶元素,增加堆中某个元素的关键字的值,返回堆顶元素
各个操作的伪代码:
这里假设数组从1开始。
(1)MAX-HEAPIFY(A, i)
l = LEFT(i)
r = RIGHT(i) //l, r分别是元素i的左子树,右子树
if(l <= A.size && A[l] > A[i])
largest = A[l]
if(r <= A.size && A[r] > A[largest])
largest = A[r]
if(largest != i){
swap(A[i], A[largest]
MAX-HEAPIFY(A, largest)
}
(2)BUILD-MAX-HEAP(A)
A.heap-size = A.length
for(i = A.length/2; i >= 1; --i) { //i的初始值为最后一个元素的父母节点
MAX-HEAPIFY(A, i)
}
(3)HEAPSORT(A)
for(i = a.length; i >= 2; --i){
swap(A[i], A[1])
--A.heap-size
MAX-HEAPIFY(A, 1)
}
(4)HEAP-EXTRACT-MAX(A)
if(A.heap-size < 1) //A从1开始,此时堆为空,报错
error
max = A[1]
swap(A[1], A[A.heap-size])
--A.heap-size
MAX-HEAPIFY(A, 1)
return max
(5)HEAP-INCREASE-KEY(A, i ,key)
if(key < A[i])
error
A[i] = key
while(i > 1 && A[PARENT(i)] < A[i]){ //PARENT(i) 表示i的双亲节点, 根据A是从0计算还是从1计算,算法不同
swap(A[PARENT(i), A[i])
i = PARENT(i)
}
(6)MAX-HEAP-INSERT(A, key)
++A.heap-size
MAX-HEAPIFY(A, 1) = -∞
HEAP-INCREASE-KEY(A, A.heap-size, key)
其实还可以增加一个HEAP-DECREASE-KEY(A, i, key)操作,用来减少下标为i的元素的关键字的值
HEAP-DERASE-KEY(A, i, key)
if(A[i] < key)
error
A[i] = key
MAXHEAPFIY(A, i)
假如A在修改i之前就是一个大根堆,那么只需要从i向下调整这个子树就可以了,不知道为什么算法导论里把这个操作放到了小根堆里
假如要使用堆管理元素e,那么应该在e中增加一个属性e.index,即表示该元素在堆数组中的下标,同时,对于堆数组中的每个元素e_1,应该有两个属性值,即对应元素e的指针,以及该元素的关键字。
为什么要这么做?因为这样的话可以在应用程序中增大了某个元素e的关键字的值之后,可以很方便的调用HEAP-INCREASE-KEY(A, e.index, key)来修改堆,同时在修改堆的时候,可以使用e_1.e->index = X,来修改实际使用的e的元素中的index值。
我们是为了使用堆才建堆的。仅仅对数组排个序,或者取个最大值根本不需要用堆