堆排序
堆:二叉堆是一个近似的完全二叉树,树上的每一个节点对应数组中的一个元素。除了最底层外,该树是充满的,而且是从左到右填充
A.length通常表示数组元素的个数,A.heap-size表示有多少个堆元素储存在数组中
PARENT(i)
return i/2(向下取整)
LEFT(i)
return 2i
RIGHT(i)
return 2i+1
//维护堆得性质,MAX-HEAPIFY是用于维护最大对性质的重要过程
//假定A[i]的左子与右子均是最大堆,但A[i]可能小于两个子节点
MAX-HEAPIFY(A,i)
l = LEFT(i)
r = RIGHT(i)
if i <= A.heapsize and A[l] > A[i]
largest = l
else largest = i
if i <= A.heapsize and A[r] > A[i]
largest = r
if largest != i
exchange A[i] with A[largest]
MAX-HEAPIFY(A,largest)
建堆
我们可以使用自下而上的形式来构建一个堆
观察发现:n/2+1…n 这些元素都是叶子,或者也可以视为一个最大堆,那我们就对这些节点的parent调用
MAX-HEAPIFY来构造这一个堆。
BUILD-HEAP(A)
A.heapsize = A.length
from n/2 down to 1
Max-HEAPIFY(A,i)
每次调用MAX_HEAPIFY的复杂度是O(logn),BUILD-HEAP的需要O(n)次这样的调用,所以这个过程的复杂度是O(nlogn),但是,这只是一个上界,但不是确界
利用如下性质的到一个更接近确界的上界:对一个有着n个节点的堆,
最多有(下界) l o g n logn logn层
每层的节点数最多是(上界) n / 2 h + 1 n/2^{h+1} n/2h+1
在每个节点上MAX-HEAPIFY的代价是O(h),
总代价为
∑
h
=
0
l
o
g
n
(
上
界
)
(
n
/
2
h
+
1
)
O
(
h
)
=
O
(
n
∑
h
=
0
n
l
o
g
n
(
上
界
)
(
h
/
2
h
+
1
)
)
\sum_{h = 0}^{logn(上界)}(n/2^{h+1})O(h) = O(n\sum_{h = 0}^{nlogn(上界)}(h/2^{h+1}))
h=0∑logn(上界)(n/2h+1)O(h)=O(nh=0∑nlogn(上界)(h/2h+1))
∑
h
2
h
=
2
\sum \frac{h}{2^{h}} = 2
∑2hh=2
于是,我们可以得出,构建一个堆的复杂度为O(n)
堆排序
HEAPSORT(A)
BUILD-HEAP(A)
for i = A.length down to 2
exchange A[i] with A[1]
A.heapsize = A.heapsize-1
MAX-HEAPIFY(A,1)
优先队列
HEAP-EXTRACT-MAX(A)
if A.heapsize < 1
error "heap underflow"
max = A[1]
A[1] = A[A.heapsize]
A.heapsize = A.heapsize-1
MAX-HEAPIFY(A,1)
return max
改变某节点的值
HEAP-INCREASE-KEY(A,i,key)
if key < A[i]
error "the key is smaller"
A[i] = key
while(A[PARENT(i)] < A[i])
exchange A[i] with A[PARENT(i)]
i = PARENT(i)
插入
HEAP-INSERT(A,key)
A.heapsize = A.heapsize+1
A[A.heapsize] = -∞
HEAP-INCREASE-KEY(A,A.heapsize,key)