算法导论 — 6.4 堆排序算法

笔记

堆排序算法一开始调用BUILD-MAX-HEAP将输入数组 A [ 1.. n ] A[1..n] A[1..n]构建成最大堆。此时,数组中的最大元素在根结点 A [ 1 ] A[1] A[1],因此通过将 A [ 1 ] A[1] A[1] A [ n ] A[n] A[n]交换,可以将 A [ 1 ] A[1] A[1]放置在排序后的正确位置。由于根结点换了一个新的元素,现在的根结点有可能不满足堆的性质,因此需要调用MAX-HEAPIFY来维护堆的性质,只不过MAX-HEAPIFY是针对 A [ 1.. n − 1 ] A[1..n-1] A[1..n1]来调用的。然后堆排序算法重复这一过程,直到堆的大小一直降到 1 1 1为止。
  在这里插入图片描述
  堆排序的时间复杂度为 O ( n l g n ) O(n{\rm lg}n) O(nlgn)。因为一共调用了 n − 1 n-1 n1次MAX-HEAPIFY,每次调用的时间为 O ( l g n ) O({\rm lg}n) O(lgn);而只调用了一次BUILD-MAX-HEAP,调用时间为 O ( n ) O(n) O(n)
  下图给出了一个堆排序的例子。
  在这里插入图片描述

练习

6.4-1 参照图6-4的方法,说明HEAPSORT在数组A = <5, 13, 2, 25, 7, 17, 20, 8, 4>上的操作过程。
  
  在这里插入图片描述

6.4-2 试分析在使用下列循环不变式时,HEAPSORT的正确性:
  在算法的第2~5行for循环每次迭代开始时,子数组 A [ 1.. i ] A[1..i] A[1..i]是一个包含了数组 A [ 1.. n ] A[1..n] A[1..n]中最小的 i i i个元素的最大堆,而子数组 A [ i + 1.. n ] A[i+1..n] A[i+1..n]包含了数组 A [ 1.. n ] A[1..n] A[1..n]中已排好序的最大的 n − i n-i ni个元素。
  
  (1) 初始状态
  此时 i = n i = n i=n,子数组 A [ 1.. i ] A[1..i] A[1..i]是一个包含了所有元素并且已经建好了的最大堆,而子数组 A [ i + 1.. n ] A[i+1..n] A[i+1..n]为空。因此,在进入迭代之前,循环不变式为真。
  (2) 保持
  假设在第 i i i次迭代之前,循环不变式为真。那么此时子数组 A [ 1.. i ] A[1..i] A[1..i]包含了整个数组 A [ 1.. n ] A[1..n] A[1..n]中最小的 i i i个元素,并且 A [ 1.. i ] A[1..i] A[1..i]是一个最大堆,因此 A [ 1 ] A[1] A[1]保存了子数组 A [ 1.. i ] A[1..i] A[1..i]中的最大元素,也就是整个数组 A [ 1.. n ] A[1..n] A[1..n]中第 n − i + 1 n-i+1 ni+1大的元素(有 n − i n-i ni个元素比它大,所以按从大到小顺序,它是第 n − i + 1 n-i+1 ni+1个)。
  迭代过程的第一步是将这个第 n − i + 1 n-i+1 ni+1大的元素从 A [ 1.. i ] A[1..i] A[1..i]中取出,并与最大堆 A [ 1.. i ] A[1..i] A[1..i]的末尾元素 A [ i ] A[i] A[i]交换。此时 A [ i ] A[i] A[i]变成了第 n − i + 1 n-i+1 ni+1大的元素,它与 A [ i + 1.. n ] A[i+1..n] A[i+1..n]组成了一个子数组 A [ i . . n ] A[i..n] A[i..n],并且 A [ i . . n ] A[i..n] A[i..n]包含了已排好序的最大的 n − i + 1 = n − ( i − 1 ) n-i+1 = n-(i-1) ni+1=n(i1)个元素。因此进入下一次迭代 ( i − 1 ) (i-1) (i1)之前,循环不变式的后半部分为真。
  上一步将最大堆 A [ 1.. i ] A[1..i] A[1..i]中最大元素取出了,并且将堆的末尾元素交换到了 A [ 1 ] A[1] A[1]的位置,因此堆的大小减小了 1 1 1,并且 A [ 1 ] A[1] A[1]位置有可能不满足最大堆的性质了。因此,迭代过程的第二步是将 h e a p _ s i z e heap\_size heap_size 1 1 1,并调用MAX-HEAPIFY(A, 1)来维持最大堆的性质。注意,此时最大堆已经变成了 A [ 1.. i − 1 ] A[1..i-1] A[1..i1],并且它包含了整个数组 A [ 1.. n ] A[1..n] A[1..n]中最小的 i − 1 i-1 i1个元素。因此进入下一次迭代 ( i − 1 ) (i-1) (i1)之前,循环不变式的前半部分也为真。
  (3) 终止
  前面说明了在每次迭代之前,循环不变式都为真。循环终止时,有 i = 1 i = 1 i=1,此时循环不变式也应当为真。将 i = 1 i = 1 i=1代入循环不变式,得到“ A [ 1 ] A[1] A[1]实际上是整个数组 A [ 1.. n ] A[1..n] A[1..n]中的最小元素,子数组 A [ 2.. n ] A[2..n] A[2..n]包含了整个数组 A [ 1.. n ] A[1..n] A[1..n]中已排好序的最大的 n − 1 n-1 n1个元素”。显然, A [ 1 ] A[1] A[1]已经是最小的元素,并且 A [ 1 ] A[1] A[1]之后的元素已经排好序,所以整个数组 A [ 1.. n ] A[1..n] A[1..n]都已经排好序。

6.4-3 对于一个按升序排列的包含 n n n个元素的有序数组 A A A来说,HEAPSORT的时间复杂度是多少?如果 A A A是降序呢?
  
  我们要假设数组中的元素各不相同。无论数组按升序排列还是按降序排序,HEAPSORT的时间复杂度都为 O ( n l g n ) O(n{\rm lg}n) O(nlgn)
  如果数组中的元素全部相同,那么HEAPSORT的时间复杂度降为 O ( n ) O(n) O(n)。因为此时for循环中的MAX-HEAPIFY的时间复杂度都为O(1)。

6.4-4 证明:在最坏情况下,HEAPSORT的时间复杂度是 Ω ( n l g n ) Ω(n{\rm lg}n) Ω(nlgn)
  
  由于建堆的时间属于低阶项,我们这里不考虑建堆的时间,只考察建堆后的排序时间。每次迭代交换 A [ 1 ] A[1] A[1] A [ i ] A[i] A[i]之后,此时堆中一共有 i − 1 i-1 i1个元素,并且此时堆的高度为 ⌊ l g ⁡ ( i − 1 ) ⌋ ⌊{\rm lg}⁡(i-1)⌋ lg(i1) A [ i ] A[i] A[i]被交换到 A [ 1 ] A[1] A[1]的位置后,再调用MAX-HEAPIFY来维护堆的性质。最坏情况下 A [ i ] A[i] A[i]会被逐层下降到最底层为止,下降的次数等于此时堆的高度 ⌊ l g ⁡ ( i − 1 ) ⌋ ⌊{\rm lg}⁡(i-1)⌋ lg(i1)。因此,最坏情况下,HEAPSORT调用MAX-HEAPIFY的总的开销为
   ∑ i = 2 n ⌊ l g ( i − 1 ) ⌋ ≥ ∑ i = 2 n ( l g ⁡ ( i − 1 ) − 1 ) = ∑ i = 2 n l g ⁡ ( i − 1 ) − ( n − 1 ) = l g ( ( n − 1 ) ! ) − ( n − 1 ) = Θ ( n l g n ) \sum_{i=2}^n⌊{\rm lg}(i-1)⌋ ≥\sum_{i=2}^n({\rm lg}⁡(i-1)-1) =\sum_{i=2}^n{\rm lg}⁡(i-1)-(n-1)={\rm lg}((n-1)!)-(n-1)=Θ(n{\rm lg}n) i=2nlg(i1)i=2n(lg(i1)1)=i=2nlg(i1)(n1)=lg((n1)!)(n1)=Θ(nlgn)
  这里用到了第3章的结论 l g ( n ! ) = Θ ( n l g n ) {\rm lg}(n!) = Θ(n{\rm lg}n) lg(n!)=Θ(nlgn)。由此可见,最坏情况下,HEAPSORT的时间复杂度是 Ω ( n l g n ) Ω(n{\rm lg}n) Ω(nlgn)

6.4-5 证明:在所有元素都不同的情况下,HEAPSORT的时间复杂度是 Ω ( n l g n ) Ω(n{\rm lg}n) Ω(nlgn)
  上题证明了堆排序的最坏情况时间复杂度为 Ω ( n l g n ) Ω(n{\rm lg}n) Ω(nlgn)。本题显然还需要证明最好情况下的时间复杂度也为 Ω ( n l g n ) Ω(n{\rm lg}n) Ω(nlgn)。不过这一证明过程较为复杂,我们记住结论即可。

以下是堆排序的代码链接。
  https://github.com/yangtzhou2012/Introduction_to_Algorithms_3rd/tree/master/Chapter06/HeapSort

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值