一、堆概念
(二叉)堆是一个数组,它可以看成一个近似的完全二叉树,树上的每个结点对应数组中的一个元素。除了最底层外,该树是完全充满的,而且是从左向右填充的
它有这样的性质:
对于给定一个结点的下标 i ,很容易计算得到它的父节点、左孩子、右孩子的下标:
父节点下标:i >> 1
左孩子下标:i << 1
右孩子下标:( i << 1 ) + 1
二叉堆分为最大堆和最小堆:
在最大堆中,除了根以外的结点 i 都要满足:A[ i >> 1] >= A[ i ],而在最小堆中,则满足 A[ i >> 1] <= A[ i ]
二、堆排序(使用最大堆)
堆排序需要下列基本例程:
调整堆例程,它输入为一个数组A和一个下标 i ,在调用调整堆例程时,它会将以 i 为根的堆调整为最大堆调整堆:调整堆为最大堆
构建堆:创建一个最大堆
堆排序:排序
调整堆(A,i)
l = left(i) // i << 1
r = right(i) //(i << 1) + 1
if l <= A.heap-size && A[ l ] > A[ i ]
largest = l
else
largest = i
if r <= A.heap-size && A[ r ] > A[ largest ]
largest = r
if i != largest
交换A[ i ] 和 A[ largest ]
调整堆(A,largest)
构建堆例程以数组 A 作为输入,该例程可以将A[ 1..n ] 转换为最大堆,可以证明:子数组A[ n/2 + 1 .. n ]中的元素都是树的叶子结点。
构建堆(A)
A.heap-size = A.length
for i = A.length/2 .. 1 step = -1 //叶子结点可以看成只有一个元素的最大堆,不需要调整
调整堆(A,i)
堆排序算法:
利用最大堆的上述2个例程,可以轻松实现堆排序算法:
·拿掉最大堆根结点,此节点为序列中的最大值,作为一次输出
·将堆尾结点换到根结点处,形成新堆,新堆长度减 1
·调整新堆为最大堆
堆排序(A)
构建堆(A)
for i = A.length .. 2 step = -1
交换A[ 1 ]与A[ i ]
--A.heap-size //堆长度减 1
调整堆(A,1)
堆排序算法的时间复杂度被证明为拥有对数复杂度O(nlgn)