在第二章介绍了两种排序算法,第六章将介绍第三种排序算法:堆排序(heapsort),以及基于堆排序的优先队列。
空间原址性(in place):仅有常数个元素需要在排序过程中存储在数组之外。
堆(6.1, P84)
堆,也叫 二叉堆,是一个数组,可以看作近似的完全二叉树,树的每个节点对应数组一个元素。
表示堆的数组A
包括两个属性:A.length
给出数组元素的个数;A.heap-size
给出有多少个元素存储在该数组中。即heap-size是数组的有效元素。
给定下标i
,很容易计算其父节点、左节点和右节点:
这三个函数通常以宏或者内联函数的方式实现。
二叉堆分为两种形式:最大堆和最小堆。
最大堆满足:A[PARENT(i)] ≥ A[i] ,即:某个节点的值最多与其父节点一样大;最小堆满足:A[PARENT(i)] ≤ A[i]。
堆排序算法采用的是最大堆。最小堆通常用于构造优先队列。
堆的高度为:$Θ(lgn)$
维持堆的性质(6.2,P86)
MAX-HEAPIFY
:输入为一个数组A和一个下标i,A[i]有可能小于其孩子,通过让A[i]在数组中“逐级下降”,从而使得以下标i为根节点的子树重新遵循最大堆的性质。
该函数伪码表示为:
算法图示:
Python实现为:
每个孩子的子树最多为2n/3(不太理解这句话??)。
所以,在最差情况下(最底层恰好半满)运行时间为:
$$T(n) = T(2n/3)+ \Theta(1)$$
上述递归式的解为:$T(n) = \text{O} (\text{lg}n)$
建堆(6.3, P87)
子数组元素$A[ (\lfloor n/2\rfloor +1),\cdots,n]$是树中的所有叶节点。BUILD_MAX_HEAP
从非叶节点开始一直循环到根节点。
Python实现为:
BUILD_MAX_HEAP
的时间复杂度为$T(n) = \text{O}(n)$
堆排序算法(6.4,P89)
伪代码:
Python实现:
HEAPSORT
过程的时间复杂度为:$\text{O}(n\text{lg}n)$,因为BUILD_MAX_HEAP
的时间复杂度为$\text{O}(n)$,n-1次调用MAX_HEAPIFY
,每次时间为$\text{O}(\text{lg}n)$。
堆排序的Python完整实现
|
|
优先队列(6.5,P90)
优先队列:是一种用来维护由一组元素构成的集合S的数据结果,其中的每个元素都有一个相关的值,称为关键字。优先队列也有两种形式:最大优先队列和最小优先队列。
最大优先队列的应用:共享计算机系统的作业调度。
最小优先队列被用于基于事件驱动的模拟器。队列中保存要模拟的事件,每个事件都有一个发生事件作为关键词。
优先队列可以用堆来实现。优先队列的元素对应应用程序的对象,堆中每个元素存储对象的句柄(handle)。
最大优先队列支持:
- 对最大优先队列进行插入,
MaxHeapInsert
; - 返回最大优先队列的最大值,
HeapMax
; - 去掉最大值并且返回该值,
HeapExtractMax
; - 将第x个元素的值改为k,其中k>=x的原来的值,
HeapIncreaseKey
;
|
|
HeapExtractMax
的操作复杂度为$\text{O}(\text{lg}n)$(也就是MAX_HEAPIFY
的复杂度)。
最大优先队列的Python完整实现:
|
|
算法基本思想:在末尾新插入一个元素,按照最大堆的要求排列好就行。
参考资料
- 算法导论 中文 第三版
- 算法导论 第六章:堆排序
- 最大优先队列–【算法导论】