堆排序的时间复杂度

1 引言

  网上对堆排序都已经讲得很清楚,唯一不清楚的部分是运行时间即时间复杂度的分析,因此本篇博客通过《算法导论》里面堆排序的内容,详细讲解堆排序的时间复杂度。

2 堆排序

  堆是一个完全二叉树,任意一节点的值大于子节点的值称为大根堆,任意一节点的值小于子节点的值称为小根堆。一般来说,堆可以用一个一维数组 A [ n ] A[n] A[n]表示。

2.1 基本操作

2.1.1 获取父、子节点

  由于堆由数组表示,因此可以根据下标计算父子节点,其运行时间为 Θ ( 1 ) \Theta(1) Θ(1)
PARENT(i)

return i/2

LEFT(i)

return 2i

RIGHT(i)

return 2i+1

2.1.2 维护堆的性质

  操作MAX-HEAPIFY(A,i)是维护大根堆的性质,其作用是,在元素 i i i的子树都是大根堆的情况下,使以 i i i为根节点的子树遵循大根堆的性质;此操作的伪代码如下:
MAX-HEAPIFY(A,i)

i = LEFT(i)
r = RIGHT(i)
if l <= A.heap-size and A[l] > A[i]
	largest = l
else 
	largest = i
if r <= A.heap-size and A[r] > A[largest]
	largest  = r
if largest != i
	exchange A[i] with A[largest]
	MAX-HEAPIFY(A,largest)

  伪代码前两个if是找到元素i以及其子节点三者中最大值的节点的下标,第三个if是将i与子节点中最大值交换,并且递归调用MAX-HEAPIFY。
  操作MAX-HEAPIFY(A,i)中除了递归调用MAX-HEAPIFY(A,i)的操作之外,其余的操作的运行时间均为 Θ ( 1 ) \Theta(1) Θ(1);那么MAX-HEAPIFY对于子树的时间代价最多为多少呢,可以用如下递归式表示:
T ( n ) ≤ T ( 2 n / 3 ) + Θ ( 1 ) T(n) \leq T(2n/3) + \Theta(1) T(n)T(2n/3)+Θ(1)
其中 Θ ( 1 ) \Theta(1) Θ(1)代表着其余非递归的操作, T ( 2 n / 3 ) T(2n/3) T(2n/3)代表子树递归调用的最大值,因为每个孩子节点的子树最多为 2 3 n \frac{2}{3}n 32n(当最底层恰好半满的时候)。
例子
如上图,就是一个半满的完全二叉树,假设节点的右子树节点个数为 2 k − 1 2^k - 1 2k1,那么左子树节点个数为 2 k + 1 − 1 2^{k+1} - 1 2k+11,总节点个数为 2 k + 2 k + 1 − 1 2^k + 2^{k+1} -1 2k+2k+11,左子树占总数的比例为: 2 k + 1 − 1 2 k + 2 k + 1 − 1 \frac{2^{k+1} - 1}{2^k + 2^{k+1} -1} 2k+2k+112k+11,当 k → ∞ k\rightarrow \infty k时,这个比例为 2 3 \frac23 32。因此可以用表达式 T ( n ) ≤ T ( 2 n / 3 ) + Θ ( 1 ) T(n) \leq T(2n/3) + \Theta(1) T(n)T(2n/3)+Θ(1)来刻画MAX-HEAPIFY操作的运行时间;根据主定理(主定理参考之前的分治策略)求解得到运行时间为 T ( n ) = O ( l g n ) T(n)=O(lgn) T(n)=O(lgn),即高为 h h h的堆的运行时间为 O ( h ) O(h) O(h)

2.2 建堆

  建堆是从堆从小到大的非叶子节点开始,堆中 ⌊ n 2 ⌋ \lfloor \frac n2\rfloor 2n以上的都是叶子节点。因此建堆过程BUILD-MAX-HEAP的伪代码如下:
BUILD-MAX-HEAP(A)

A.heap-size = A.length
for i = A.length/2 downto 1
	MAX-HEAPIFY(A,i)

由上述伪代码中,每次调用MAX-HEAPIFY的时间为 O ( l g n ) O(lgn) O(lgn),总共调用 O ( n ) O(n) O(n)次,因此可以估算建堆的运行时间为 O ( n l g n ) O(nlgn) O(nlgn),但是很明显,这个上界不够精确。
  MAX-HEAPIFY的时间与树的高度有关,而大多数节点高度都不高。利用如下性质可以得到更精确的运行时间:高度为 h h h的堆最多包含 ⌈ n 2 h + 1 ⌉ \lceil \frac{n}{2^{h+1}} \rceil 2h+1n个节点;
  在一个高度为 h h h的节点上运行MAX-HEAPIFY的代价是 O ( h ) O(h) O(h),因此建堆(BUILD-MAX-HEAP)的总代价为:
∑ h = 0 ⌊ l g n ⌋ ⌈ n 2 h + 1 ⌉ O ( h ) = O ( n ∑ h = 0 ⌊ l g n ⌋ h 2 h ) \sum_{h=0}^{\lfloor lgn \rfloor}\lceil \frac{n}{2^{h+1}} \rceil O(h)=O(n\sum_{h=0}^{\lfloor lgn \rfloor}\frac{h}{2^h}) h=0lgn2h+1nO(h)=O(nh=0lgn2hh)
其中有: ∑ h = 0 ∞ h 2 h = 2 \sum_{h=0}^{\infty}\frac{h}{2^h}=2 h=02hh=2(根据公式: ∑ k = 0 ∞ k x k = x ( 1 − x ) 2 \sum_{k=0}^{\infty}kx^k=\frac{x}{(1-x)^2} k=0kxk=(1x)2x);
因此有:
O ( n ∑ h = 0 ⌊ l g n ⌋ h 2 h ) = O ( n ∑ h = 0 ∞ h 2 h ) = O ( n ) O(n\sum_{h=0}^{\lfloor lgn \rfloor}\frac{h}{2^h})=O(n\sum_{h=0}^{\infty}\frac{h}{2^h})=O(n) O(nh=0lgn2hh)=O(nh=02hh)=O(n)
  所以,建堆所花时间为 O ( n ) O(n) O(n)

2.3 堆排序算法

  堆排序算法先建立堆,然后通过堆与末尾元素互换,调整堆,重复这个过程,最终完成排序,其伪代码如下:
HEAPSORT(A)

BUILD-MAX-HEAP(A)
for i = A.length downto 2
	exchange A[1] with A[i]
	A.heap-size = A.heap-size - 1
	MAX-HEAPIFY(A, 1)

其中,BUILD-MAX-HEAP运行时间为 O ( n ) O(n) O(n),执行了 n − 1 n-1 n1次MAX-HEAPIFY,每次时间为 O ( l g n ) O(lgn) O(lgn),因此,堆排序的时间复杂度为 O ( n l g n ) O(nlgn) O(nlgn)

  • 6
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值