学习笔记 | 《算法导论》之从入门到放弃(3)
算法导论打卡3,主要内容:堆排序
第六章 堆排序
堆
- 二叉堆是一个数组,它可以被看成一个近似的完全二叉树。树上的每一个结点对应数组中的一个元素。除了最底层外,该树是完全充满的,而且是从左向右填充。
- 表示堆的数组A包括两个属性:A.length(通常)给出数组元素的个数,A.heap-size表示有多少个堆元素存储在该数组中。也就是说虽然A[1..A.length]可能都存有数据,但只有A[1..A.heap-size]中存放的是堆的有效元素,这里0≤A.heap-size≤A.length。树的根节点是A[1],这样给定一个结点的下标i,我们很容易计算得到它的父结点,左孩子和右孩子的下标:
1 | def parent(i): |
- 二叉堆可以分为两种形式:最大堆和最小堆。在这两种堆中,结点的值都要满足堆的性质。区别在于:
- 最大堆中,最大堆性质是指除了根以外的所有结点i都要满足:A[PARENT(i)]≥A[i],即在任一子树中,该子树所包含的所有结点的值都不大于该子树根节点的值。
- 最小堆中,最小堆性质是指除了根以外的所有结点i都要满足:A[PARENT(i)]≤A[i]
- 在堆排序算法中,我们使用最大堆。最小堆通常用于构造优先队列。
- 如果把堆看成一棵树,我们定义一个堆中的结点的高度是该节点到叶结点最长简单路径上
边的数目
。既然一个包含n个元素的堆可以看作是一颗完全二叉树,那么该堆的高度是O(lgn)
- 堆结构上的一些基本操作的运行时间至多与树的高度成正比,即时间复杂度为O(lgn).
维护堆的性质
- 通过让A[i]的值在最大堆中“逐级下降”,从而使得以下标i为根结点的子树重新遵循最大堆的性质:
1 | def max_heapify(A,i): |
- 对于一个高为h的结点来说,max_heapify的时间复杂度是O(h)。
建堆
1 | def build_max_heap(A): |
- 完整代码:
1 | def max_heapify(A,i): |
堆排序算法
- 因为数组的最大元素总是在根结点A[1]中,通过把它与A[n]进行互换,我们可以把该元素放到正确的位置上(如从小到大排列)
1 | def max_heapify(A,i): |
- heapsort的时间复杂度是O(nlgn),因为每次调用build_max_heap的时间复杂度是O(n),而n-1次调用max_heapify,每次的时间是O(lgn)。
优先队列
- 优先队列是一种用来维护由一组元素构成的集合S的数据结构,其中的每一个元素都有一个相关的值,称为关键字。
- 一个最大优先队列支持以下操作:
- insert(S,x):把元素x插入集合S中。
- maximum(S):返回S中具有最大关键字的元素。
- extract-max(S):去掉并返回S中具有最大关键字的元素。
- increase-key(S,x,k):将元素x的关键字值增加到k,这里假设k的值不小于x的原关键字值。
- 最小优先队列同理
- 实现:
1 | def heap_maximum(A): |
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 Justlovesmile's BLOG!
评论