分类目录:《算法设计与分析》总目录
相关文章:
·堆(一):基础知识
·堆(二):维护堆的性质
·堆(三):建堆
·堆(四):优先队列
·排序算法:堆排序
·利用heapq模块实现堆
heapify(arr, i)
是用于维护最大堆性质的重要过程。它的输入为一个数组
A
A
A和一个下标
i
i
i。
在调用heapify(arr, i)
的时候,我们假定根结点为left(i)
和right(i)
的二叉树都是最大堆,但这时
A
[
i
]
A[i]
A[i]有可能小于其孩子,这样就违背了最大堆的性质。heapify(arr, i)
通过让
A
[
i
]
A[i]
A[i]的值在最大堆中“逐级下降”,从而使得以下标
i
i
i为根结点的子树重新遵循最大堆的性质。
def heapify(arr, i):
largest = i
l = 2 * i + 1 # left = 2*i + 1
r = 2 * i + 2 # right = 2*i + 2
if l < len(arr) and arr[i] < arr[l]:
largest = l
if r < len(arr) and arr[largest] < arr[r]:
largest = r
if largest != i:
arr[i],arr[largest] = arr[largest],arr[i] # 交换
heapify(arr, largest)
下图图示了heapify(arr, i)
的执行过程。在程序的每一步中,从
A
[
i
]
A[i]
A[i]、
A
[
l
e
f
t
(
i
)
]
A[left(i)]
A[left(i)]和
A
[
r
i
g
h
t
(
i
)
]
A[right(i)]
A[right(i)]中选出最大的,并将其下标存储在
l
a
r
g
e
s
t
largest
largest中。如果
A
[
i
]
A[i]
A[i]是最大的,那么以
i
i
i为根结点的子树已经是最大堆,程序结束。否则,最大元素是
i
i
i的某个孩子结点,则交换
A
[
i
]
A[i]
A[i]和
A
[
l
a
r
g
e
s
t
]
A[largest]
A[largest]的值。从而使
i
i
i及其孩子都满足最大堆的性质。在交换后,下标为
l
a
r
g
e
s
t
largest
largest的结点的值是原来的
A
[
i
]
A[i]
A[i],于是以该结点为根的子树又有可能会违反最大堆的性质。因此,需要对该子树递归调用heapify(arr, i)
。
对于一棵以
i
i
i为根结点、大小为
n
n
n的子树,heapify(arr, i)
的时间代价包括:调整
A
[
i
]
A[i]
A[i]、
A
[
l
e
f
t
(
i
)
]
A[left(i)]
A[left(i)]和
A
[
r
i
g
h
t
(
i
)
]
A[right(i)]
A[right(i)]的关系的时间代价
Θ
(
1
)
\Theta(1)
Θ(1),加上在一棵以
i
i
i的一个孩子为根结点的子树上运行heapify(arr, i)
的时间代价(这里假设递归调用会发生)。因为每个孩子的子树的大小至多为
2
n
3
\frac{2n}{3}
32n(最坏情况发生在树的最底层恰好半满的时候),我们可以用下面这个递归式刻画heapify(arr, i)
的运行时间:
T
(
n
)
≤
T
(
2
n
3
)
+
Θ
(
1
)
T(n)\leq T(\frac{2n}{3}) + \Theta(1)
T(n)≤T(32n)+Θ(1)
故递归式的解为
T
(
n
)
=
O
(
log
n
)
T(n)=O(\log n)
T(n)=O(logn)。也就是说,对于一个树高为
h
h
h的结点来说,heapify(arr, i)
的时间复杂度是
O
(
h
)
O(h)
O(h)。