第5章 堆排序
5.1 堆
-
堆:(二叉)堆是一个数组,它可以被看成一个近似的完全二叉树。
-
最大堆性质:除了根以外的所有结点 i i i都要满足: A [ P A R E N T ( i ) ] > = A [ i ] A[PARENT(i)]>=A[i] A[PARENT(i)]>=A[i]
-
最小堆性质:除了根以外的所有结点 i i i都要满足: A [ P A R E N T ( i ) ] < = A [ i ] A[PARENT(i)]<=A[i] A[PARENT(i)]<=A[i]
-
结点的高度:该结点到叶节点最长简单路径上边的数目。
-
常用表示:
-
堆的结点数: h e a p s i z e = n heapsize=n heapsize=n
-
堆的根结点个数: ⌊ n / 2 ⌋ \lfloor n/2\rfloor ⌊n/2⌋
-
堆的高度: ⌊ lg n ⌋ \lfloor \lg n\rfloor ⌊lgn⌋
-
高度为 h h h的堆最多包含结点数: ⌈ n / 2 k + 1 ⌉ \lceil n/2^{k+1}\rceil ⌈n/2k+1⌉
-
父结点: i i i
-
左子结点: 2 i 2i 2i
-
右子结点: 2 i + 1 2i+1 2i+1
-
5.2 维护堆的性质
-
MAX-HEAPIFY是用于维护最大堆性质的重要过程。
-
给定最大堆A中的一个元素i,它有可能不符合最大堆的性质,即A[i]有可能小于它的孩子,但是它的两棵子树已经满足了最大堆的性质。我们通过一个MAX-HEAPIFY过程来使得以A[i]为根的子树满足最大堆性质,MAX-HEAPIFY是通过让A[i]在最大堆中“逐层下降”来达成这一目标的。
-
伪代码:MAX-HEAPIFY(A,i)
l = 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 larget != i exchange A[i] with A[largest] MAX-HEAPIFY(A,largest)
-
python代码:
def max_heapify(A, i): heap_size = len(A) left = 2 * i right = 2 * i + 1 if left <= heap_size and A[left] > A[i]: largest = left else: largest = i if right <= heap_size and A[right] > A[largest]: largest = right if largest != i: temp = A[largest] A[largest] = A[i] A[i] = temp max_heapify(A, largest) return A B = [16, 4, 10, 14, 7, 9, 3, 2, 8, 1] k = 1 print(max_heapify(B, k))
-
运行时间: T ( n ) = O ( lg n ) T(n)=O(\lg n) T(n)=O(lgn)
-
时间复杂度: O ( h ) O(h) O(h)
5.3 建堆
-
初始化:在第一次循环之前, i = ⌊ n / 2 ⌋ i=\lfloor n/2\rfloor i=⌊n/2⌋,而 i = ⌊ n / 2 ⌋ + 1 , i = ⌊ n / 2 ⌋ + 2 , ⋅ ⋅ ⋅ , n i=\lfloor n/2\rfloor+1,i=\lfloor n/2\rfloor+2,···,n i=⌊n/2⌋+1,i=⌊n/2⌋+2,⋅⋅⋅,n都是叶结点,因而是平凡最大堆的根结点。
-
保持:每次迭代维护这个循环的不变量,即满足最大堆性质。
-
终止:过程终止时,满足最大堆性质。
-
伪代码:BUILD-MAX-HEAP(A)
A.heap-size = A.length for i = (A.length/2)的下界 downto 1 MAX-HEAPIFY(A,i)
-
python代码:
def build_max_heap(A): heap_size = len(A) for i in range(1, heap_size//2): max_heapify(A, i) return A B = [16, 4, 10, 14, 7, 9, 3, 2, 8, 1] print(build_max_heap(B))
5.4 堆排序算法
-
步骤:
- 将待排序的数组构造成一个大根堆,此时,整个数组的最大值就是堆结构的顶端
- 将顶端的数与末尾的数交换,此时,末尾的数为最大值,剩余待排序数组个数为n-1
- 将剩余的n-1个数再构造成大根堆,再将顶端数与n-1位置的数交换,如此反复执行,便能得到有序数组
-
伪代码: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)
-
python代码:
def max_heapify(A, i): heap_size = A[0] left = 2 * i right = 2 * i + 1 if left <= heap_size and A[left] > A[i]: largest = left else: largest = i if right <= heap_size and A[right] > A[largest]: largest = right if largest != i: temp = A[largest] A[largest] = A[i] A[i] = temp max_heapify(A, largest) return A def build_max_heap(A): heap_size = len(A) for i in range(1, heap_size//2): max_heapify(A, i) return A def heapsort(A): build_max_heap(A) for i in range(len(A)-1, 1, -1): temp = A[1] A[1] = A[i] A[i] = temp A[0] = A[0] - 1 max_heapify(A, 1) return A B = [10, 16, 4, 10, 14, 7, 9, 3, 2, 8, 1] print(heapsort(B))
5.5 优先队列(以最大优先队列为例,最小优先队列同理)
-
优先队列:优先队列是一种用来维护由一组元素构成的集合S的数据结构。
-
操作:
- INSERT(S,x):把元素x插入集合S中, S = S ⋃ { x } S=S\bigcup \{x\} S=S⋃{x}
- MAXIMUM(S):返回S中具有最大键字的元素。
- EXTRACT-MAX(S):去掉并返回S中的具有最大键字的元素。
- INCREASE-KEY(S,X,K):将元素X的关键字值增加到k。
-
伪代码:
-
MAXIMUM(S)
BUILD-MAX-HEAP(A) return A[1]
-
EXTRACT-MAX(S)
if A.heap-size < 1 error"heap underflow" max = A[1] A[1] = A[A.heap-size] A.heap-size = A.heap-size - 1 MAX-HEAPIFY(A,1) return max
-
INCREASE-KEY(S,X,K)
if key < A[i] error"newe key is smaller than current key" A[i] = key while i>1 and A[PARENT(i)]<A[1] exchange A[i] with A[PARENT(i)] i = PARENT(i)
-