0
点赞
收藏
分享

微信扫一扫

算法设计与分析——堆(二):维护堆的性质


分类目录:​​《算法设计与分析》总目录​​

​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)。



举报

相关推荐

0 条评论