堆排序
来源:互联网 发布:怎么解决淘宝自主访问 编辑:程序博客网 时间:2024/06/06 16:29
1. 堆
堆是一个数组,又近似于一个完全二叉树。
如图,树上的每一个结点对应数组中的一个元素。除了最底层外,该树是完全充满的,而且是从左向右填充的。
树的根节点是A[1],给定一个下标i,我们很容易得到它的父节点,左右孩子结点的下标。
PARENT(i)return i / 2
LEFT(i)return 2 * i
RIGHT(i)return 2 * i + 1
(在计算机中,通过将i 的值左移一位,可以计算LEFT,通过将i 的值左移一位并加1,可以快速得到RIGHT。同理,可以通过将i 的值右移一位得到PARENT。且这三个函数可通过“宏”或“内联函数”实现)
二叉堆可以分为两种形式:最大堆和最小堆。
在最大堆中,除了根节点以外所有的结点i 都要满足:A[PARENT] >= A[i]。
同理,最小堆中,除了根节点以外的所有结点都要满足:A[PARENT] <= A[i]。
PS:
堆的性质有:
- 含n个元素的堆的高度为lgn。
- 当用数组表示存储n个元素的堆时,叶结点的下标分别是n/2+1, n/2+2, … , n。
2. 堆排序–实现
2.1 维护堆的性质
假定根节点为LEFT(i)和RIGHT(i)的二叉树都是最大堆。
输入一个数组A和下标i ,使得A[i]的值在最大堆中“逐级下降”(即A[i]为堆中最大值),从而使得以下标i 为根节点的子树重新遵循最大堆的性质。
通过将当前结点i 与左右子结点对比,找到最大值,放在i 的位置(根节点),并递归调用MAX_HEAPIFY从而维护最大堆性质。
伪代码:
FUNCTION MAX_HEAPIFY(A, i)l = LEFT(i);r = RIGHT(i);if l<= A.heap_size && A[l] > A[i] largest = lelse largest = iif r<= A.heap_size && A[r] > A[largest] largest = rif largest != i exchange A[i] with A[largest] MAX_HEAPIFY(A, largest)
对于一个树高为h的结点来说,MAX_HEAPIFY的时间复杂度为O(h)。
2.2 构建最大堆
我们用自底向上的方法利用MAX_HEAPIFY把一个大小为A.heap_size的数组转换为A[1,…,n]转换为最大堆。
由堆的性质可知,子数组A[n/2 + 1….n] 中的元素都是树的叶节点。每个叶结点都可以看成是只包含一个元素的堆。
- 初始化:在for循环之前,可确定A[n/2 + 1], … , A[n]中的元素都是叶结点,因此看做是最大堆的根节点。
- 保持:利用MAX_HEAPIFY不断将结点i 添加至堆中,并维持最大堆的性质。
- 终止:当i = 0时,因为MAX_HEAPIFY不断维护最大堆性质,使得每个结点都是一个最大堆的根。
伪代码:
FUNCTION BUILD_MAX_HEAP(A)A.heap_size = A.lengthfor i = A.length / 2 downto 1 MAX_HEAPIFY(A, i)
由此,每次调用MAX_HEAPIFY的时间复杂度为O(lgn),而BUILD_MAX_HEAP需要O(n)次这样的调用,总的时间复杂度为O(n · lgn)。但这个上界并不是渐近紧确的。
因为不同的MAX_HEAPIFY的时间与当前结点的高度有关,最后计算所得,BUILD_MAX_HEAP的时间复杂度为O(n)。
2.3堆排序
因为数组中的最大元素总在根节点A[1]中,通过把它与A[n]进行互换,我们可以将最大值放在最后,从而进行由小到大的排序。这时,我们从堆中去掉结点n(因为它已经在正确的位置,通过减少A.heap_size的值来实现),剩余的结点中,原来的根的孩子结点仍是最大堆,但新的根节点可能会违背最大堆的性质,这时需要调用MAX_HEAPIFY(A,1).
FUNCTION HEAP_SORT(A)BUILD_MAX_HEAP(A)for i = A.length downto 2 exchange A[1] with A[i] A.heap_size -= 1 MAX_HEAPIFY(A, 1)
HEAP_SORT的时间复杂度为O(n · lgn)。
3. 优先队列
优先队列是一种用来维护由一组元素构成的集合S的数据结构。一个优先队列支持以下操作:
- INSERT(S, x):把元素x插入集合S中。
- MAXIMUM(S):返回S中具有最大关键字的元素。
- EXTRACT_MAX(S):去掉并返回S中的具有最大关键字的元素。
- INCREASE_KEY(S, x, k):将元素x的关键字值增加到k,假设k值不小于x的原值。
最大优先队列的应用很多,其中一个就是作业调度,调度一个优先级最大的作业执行。
MAXIMUM伪代码: 时间复杂度为O(1)。
FUNCTION HEAP_MAXIMUM(A)return A[1]
EXTRACT_MAX伪代码: 时间复杂度为O(lgn)。
FUNCTION HEAP_EXTRACT_MAX(A)if A.heap_size < 1 error"heap underflow"max = A[1]A[1] = A[A.heap_size]A.heap_size = A.heap_size - 1MAX_HEAPIFY(A, 1)return max
INCREASE_KEY伪代码: 时间复杂度为O(lgn)。
FUNCTION HEAP_INCREASE_KEY(A, i, key)if key < A[i] error"new key is smaller than current key"A[i] = keywhile i > 1 && A[PARENT(i)] < A[i] exchange A[i] with A[PARENT(i)] i = PARENT(i)
INSERT伪代码: 时间复杂度为O(lgn)。
FUNCTION HEAP_INSERT(A, key)A.heap_size = A.heap_size + 1A[heap_size] = -∞HEAP_INCREASE_KEY(A, A.heap_size, key)
- 堆及堆排序
- 堆/堆排序特点
- 【二叉堆、堆排序】
- 二叉堆 & 堆排序
- 二叉堆 & 堆排序
- 堆与堆排序
- 堆与堆排序
- 堆与堆排序
- 堆与堆排序
- 堆与堆排序
- 堆与堆排序
- 堆和堆排序
- 堆排序(最大堆)
- 堆和堆排序
- 堆和堆排序
- 堆及堆排序
- 堆和堆排序
- 堆与堆排序
- 对员工的增加,删除
- 【POJ】-2485-Highways(最小生成树)
- python TCP编程小试牛刀
- 深入理解 Java 注解 [元注解(一)]
- 工作流现状2008年
- 堆排序
- #5 Deep learning RNN-RBM简单理解
- Android图片加载与缓存开源框架:Android Glide
- Struts2 我的第一个Helloworld
- Rxlifecycle使用详解,解决RxJava内存泄露问题
- ACM解题总结——HihoCoder1199 (微软笔试题)
- Android01 Android开发环境搭建和HelloWorld
- 【CDP-云设计模式】第6章,1.写代理模式(Write Proxy Pattern)
- 使用读写锁简单实现缓存的实例