堆排序的时间复杂度

该博客聚焦堆排序算法的时间复杂度分析。先介绍堆的概念及基本操作,包括获取父子节点和维护堆性质,给出相关操作运行时间。接着分析建堆过程,得出建堆时间为O(n)。最后阐述堆排序算法,算出其时间复杂度为O(nlgn)。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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)

### 堆排序时间复杂度分析 堆排序是一种基于比较的排序算法,其时间复杂度可以通过初始化建堆和排序重建堆两个阶段进行分析。在初始化建堆的过程中,构建初始堆的时间复杂度为 \(O(n)\)。这是因为建堆时从最后一个非叶子节点开始调整堆结构,每个节点的调整操作最多需要 \(O(\log k)\),其中 \(k\) 是该节点的子树高度[^1]。 在排序过程中,每次从堆顶取出最大元素后需要重新调整堆结构以维持堆的性质,这一过程称为重建堆。重建堆的操作时间复杂度为 \(O(\log n)\),而整个排序过程需要执行 \(n-1\) 次这样的操作,因此重建堆的总时间复杂度为 \(O(n \log n)\)[^1]。 综合来看,堆排序的总时间复杂度为初始化建堆的时间复杂度 \(O(n)\) 加上排序重建堆的时间复杂度 \(O(n \log n)\),最终得到堆排序时间复杂度为 \(O(n + n \log n) = O(n \log n)\)[^1]。 此外,堆排序的比较次数与输入序列的初始状态有关。如果输入序列已经是一个堆,则比较次数会显著减少;但在输入序列有序或逆序的情况下,堆排序的比较次数不会发生明显变化[^1]。 ```python def heapify(arr, n, i): largest = i # 初始化最大值为根节点 left = 2 * i + 1 # 左子节点 right = 2 * i + 2 # 右子节点 # 如果左子节点大于根节点 if left < n and arr[left] > arr[largest]: largest = left # 如果右子节点大于当前最大值 if right < n and arr[right] > arr[largest]: largest = right # 如果最大值不是根节点,交换并递归调整子树 if largest != i: arr[i], arr[largest] = arr[largest], arr[i] heapify(arr, n, largest) def heap_sort(arr): n = len(arr) # 构建初始堆 for i in range(n // 2 - 1, -1, -1): heapify(arr, n, i) # 逐个从堆顶取出元素并重建堆 for i in range(n - 1, 0, -1): arr[i], arr[0] = arr[0], arr[i] # 将最大值移到数组末尾 heapify(arr, i, 0) # 调整剩余部分为堆 ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值