算法设计与分析——堆(三):建堆

分类目录:《算法设计与分析》总目录
相关文章:
·堆(一):基础知识
·堆(二):维护堆的性质
·堆(三):建堆
·堆(四):优先队列
·排序算法:堆排序
·利用heapq模块实现堆


我们可以用自底向上的方法利用过程heapify(arr, i)把一个大小为 n = A . l e n g t h n=A. length n=A.length的数组 A A A转换为最大堆。子数组 A ( ⌊ n 2 ⌋ + 1 ⋯ n ) A(\lfloor\frac{n}{2}\rfloor+1\cdots n) A(⌊2n+1n)中的元素都是树的叶结点。每个叶结点都可以看成只包含一个元素的堆。过程max_heap(A)对树中的其他结点都调用一次heapify(arr, i)

def max_heap(A):
	for i in range(int(len(A)/2) + 1, 0):
		heapify(A, i)

为了证明max_heap(A)的正确性,我们使用如下的循环不变量:在第2-3行中每一次for循环的开始,结点计 i + 1 , i + 2 , ⋯   , n i+1, i+2, \cdots, n i+1,i+2,,n都是一个最大堆的根结点。

我们需要证明这一不变量在第一次循环前为真,并且每次循环迭代都维持不变。当循环结束时,这一不变量可以用于证明正确性

  • 初始化:在第一次循环迭代之前, i = ⌊ n 2 ⌋ i=\lfloor\frac{n}{2}\rfloor i=2n,而 n 2 + 1 , n 2 + 2 , ⋯   , n \frac{n}{2} + 1, \frac{n}{2} + 2, \cdots, n 2n+1,2n+2,,n都是叶结点,因而是平凡最大堆的根结点。
  • 保持:为了看到每次迭代都维护这个循环不变量,注意到结点 i i i的孩子结点的下标均比 i i i大。所以根据循环不变量,它们都是最大堆的根。这也是调用heapify(A, i)使结点i成为一个最大堆的根的先决条件。而且,heapify(A, i)维护了结点 n 2 + 1 , n 2 + 2 , ⋯   , n \frac{n}{2} + 1, \frac{n}{2} + 2, \cdots, n 2n+1,2n+2,,n都是一个最大堆的根结点的性质。在for循环中递减i的值,为下一次循环重新建立循环不变量
  • 终止:过程终止时, i = 0 i=0 i=0。根据循环不变量,每个结点 1 , 2 , ⋯   , n 1, 2, \cdots, n 1,2,,n都是一个最大堆的根。特别需要指出的是,结点1就是最大的那个堆的根结点。

我们可以用下面的方法简单地估算max_heap(A)运行时间的上界。每次调用heapify(A, i)的时间复杂度是 O ( log ⁡ n ) O(\log n) O(logn)max_heap(A)需要 O ( n ) O(n) O(n)次这样的调用。因此总的时间复杂度是 O ( n log ⁡ n ) O(n\log n) O(nlogn)。当然,这个上界虽然正确,但不是渐近紧确的我们还可以进一步得到一个更紧确的界。可以观察到,不同结点运行max_heap(A)的时间与该结点的树高相关,而且大部分结点的高度都很小。因此,利用如下性质可以得到一个更紧确的界:包含 n n n个元素的堆的高度为 ⌊ lg ⁡ n ⌋ \lfloor \lg n\rfloor lgn。所以我们可以得到max_heap(A)的时间复杂度为 O ( n ) O(n) O(n)

  • 4
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

von Neumann

您的赞赏是我创作最大的动力~

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值