堆排序综合了插入排序的原址(只需要常量额外空间)和归并排序时间复杂度为O(nlg(n))的有点的一种排序方法
6.1 堆
堆是一个数组,可以被看成一个近似的完全二叉树(即用利用二叉树的数学特性,使用数组实现的一种二叉树),包含有length和size连个属性,index < size的数据有效。二叉树的下标index的左右子节点位置分别为2i,2i+1,根节点为floor(i/2):
def PARENT(i): return (i-1) // 2 def LEFT(i): return i*2 + 1 def RIGHT(i): return i*2 + 2
6.2维护堆的性质
若最大堆的某个元素破坏了,最大堆的性质(即父节点小于子节点)需要将父节点,左右节点中的最大值放置到父节点的位置,置换之后在递归的调用MAX_HEAPIFY函数维护被置换的那个位置的元素。
def MAX_HEAPIFY(A, i, size): l = LEFT(i) r = RIGHT(i) if l < size and A[l] > A[i]: largest = l else: largest = i if r < size and A[r] > A[largest]: largest = r if largest != i: temp = A[i] A[i] = A[largest] A[largest] = temp MAX_HEAPIFY(A, largest, size)在最差情况下(最底层恰好半满)其执行时间为T(n) <= T(2/3 * n) + theta(1), 由主定理解得 T(n) = O(lg(n)), 即为树的高度。
6.3 建堆
可以利用自底向上的使用方法 MAX_HEAPIFY 来将一个无序数组变成一个最大堆。
def BUILD_MAX_HEAP(A): size = len(A) for i in range(len(A)//2, -1, -1): MAX_HEAPIFY(A, i, size)
程序正确性证明:使用循环不变式,所有在i之后的元素都组成最大堆
初始化:在第一次循环迭代之前,所有i之后的元素为叶子节点没有子节点,自身组成最大堆保持:在循环i = k时,若其后的元素均为最大堆,调用MAX_HEAPIFY 方法使得A[k]与其子节点置换,故在下次迭代开始之前第k个元素之后均组成最大堆
终止:当i = 0时,其后元素(左右子树)均组成最大堆,调用MAX_HEAPIFY是的整个数组称为一个最大堆。
叶子节点个数为n/2,上一层为 n/4...高度为lg(n),第k层中每个元素的执行时间为O(lg(k))乘以每一层元素个数,在所有层求和即为其执行时间,解为O(n)。
6.4 堆排序算法
利用一个数组建一个最大堆,则第一个必为最大值,利用这个特性可以完成数组排序。
def HEAPSORT(A, size): BUILD_MAX_HEAP(A) for i in range(size-1, 0, -1): temp = A[i] A[i] = A[0] A[0] = temp size = size - 1 MAX_HEAPIFY(A, 0, size)时间复杂度BUILD_MAX_HEAP花费O(n), n次MAX_HEAPIFY花费n*O(lg(n)) = O(nlg(n)),两者相加HEAPSORT的时间复杂度为 O(nlg(n))。
6.5 优先队列
优先队列,即是一个带优先级的队列,包括最大优先队列和最小优先队列,包含以下操作:
实现如下效率分别为theta(1), theta(lg(n)), theta(lg(n)), theta(lg(n)):
def HEAP_MAXIMUM(A): return A[0] def HEAP_EXTRACT_MAX(A, size): assert(size > 0) iMax = A[0] size = size - 1 A[0] = A[size] MAX_HEAPIFY(A, 0, size) return iMax def HEAP_INCREASE_KEY(A, i, key): assert(A[i] < key) A[i] = key while i > 0 and A[PARENT(i)] < A[i]: temp = A[i] A[i] = A[PARENT(i)] A[PARENT(i)] = temp i = PARENT(i) def MAX_HEAP_INSERT(A, key): A.append(-float("inf")) HEAP_INCREASE_KEY(A, len(A)-1, key)