您的位置:首页 > 其它

堆与堆排序

2016-01-09 17:35 190 查看

转载自:http://www.cnblogs.com/AndyJee/p/4612821.html

(算法)堆与堆排序

主要内容:

1、什么是堆?

2、如何建堆

3、堆排序

4、参考代码

 

一、什么是堆?

“堆”是个很有趣的数据结构,是个完全二叉树。

“堆”的特性:每个节点的键值一定总是大于(或小于)它的父节点(大于:称为“最大堆”,小于:称为“最小堆”),或者说每个节点总是大于或小于它的子节点。

对于最大堆而言,根节点为最大值;对于最小堆而言,根节点为最小值。

通常“堆”是通过数组来实现的,这样可以利用数组的特点快速定位指定索引的元素。

因此,当我们得知某个节点的索引为 i 时,可以通过以下“堆”的定义很容易地定位到它的子节点和父节点。

在起始索引为 0 的“堆”中: 

1) 堆的根节点将存放在位置 0 

2) 节点 i 的左子节点在位置 2 * i + 1 

3) 节点 i 的右子节点在位置 2 * i + 2 

4) 节点 i 的父节点在位置 floor( (i - 1) / 2 )   : 注 floor 表示“取整”操作 

在起始索引为 1 的“堆”中: 

1) 堆的根节点将存放在位置 1 

2) 节点 i 的左子节点在位置 2 * i 

3) 节点 i 的右子节点在位置 2 * i + 1 

4) 节点 i 的父节点在位置 floor( i / 2 )           : 注 floor 表示“取整”操作 

二、如何建堆?

根据上述对于堆的定义,建立一个堆,首先需要构建一棵完全二叉树,树的节点需要满足:每个节点的值都不大于或不小于其父节点的值,即A[PARENT[i]] >=/<= A[i]。

给定一个无序数组,如何构建一个最大堆呢?

首先,将该无序数组构建为一棵完全二叉树,然后根据每个节点需要满足的条件,进行二叉树的调整。

建立一个最大堆,而建堆的核心内容是调整堆,使二叉树满足堆的定义(每个节点的值都不大于其父节点的值)。

调堆的过程应该从最后一个非叶子节点(最后一个节点的父节点)开始,假设有数组A = {1, 3, 4, 5, 7, 2, 6, 8, 0}。

那么调堆的过程如下图,数组下标从0开始,A[3] = 5开始,分别与左孩子和右孩子比较大小,如果A[3]最大,则不用调整,否则和孩子中的值最大的一个交换位置,在图1中是A[7] > A[3] > A[8],所以A[3]与A[7]对换,从图1.1转到图1.2。



 

所以建堆的过程就是

?
调堆:

如果初始数组是非降序排序,那么就不需要调堆,直接就满足堆的定义,此为最好情况,运行时间为Θ(1);

如果初始数组是如图1.5,只有A[0] = 1不满足堆的定义,经过与子节点的比较调整到图1.6,但是图1.6仍然不满足堆的定义,所以要递归调整,一直到满足堆的定义或者到堆底为止。

如果递归调堆到堆底才结束,那么是最坏情况,运行时间为O(h) (h为需要调整的节点的高度,堆底高度为0,堆顶高度为floor(logn) )。

调堆的时间复杂度:O(logn)

建堆的时间复杂度:O(n)

关于时间复杂度的分析:参考http://www.cnblogs.com/zabery/archive/2011/07/26/2117103.html

三、如何进行堆排序?

根据“堆”的特性,我们可以从无序序列的最后一个节点的父节点开始,对其进行“下移”操作,直到序列的第一个节点为止。这样就可以保证每个节点都在合适的位置上,也就建立起了一个“堆”。 
但是“堆”并不是一个完全排序的序列,因为“堆”只保证了父节点与子节点的位置关系,但并不保证左右子节点的位置关系。那么我们如何进行“堆排序”呢? 

由于一个“堆”的根节点必然是整个序列中最大的元素,因此对于一个排序的序列而言,每个“堆”中我们只能得到一个有效的元素。如果我们拿掉根节点,再对剩下的序列重新排列组成一个“堆”,反复如此,我们就可以依次得到一个完整的排序序列了。 

当然,为了简化操作,每次我们只需要把根节点与最后一个位置的节点交换,然后把最后一个位置排除之外,对根节点进行“下移”操作即可。

堆排序的时间复杂度:O(nlogn)

关于时间复杂度的分析:参考http://www.cnblogs.com/zabery/archive/2011/07/26/2117103.html

建堆完成之后,堆如图1.7是个大根堆。

将A[0] = 8 与 A[heapLen-1]交换,然后heapLen减一,如图2.1,然后AdjustHeap(A, heapLen-1, 0),如图2.2。如此交换堆的第一个元素和堆的最后一个元素,然后堆的大小heapLen减一,对堆的大小为heapLen的堆进行调堆,如此循环,直到heapLen == 1时停止,最后得出结果如图3。

 

四、代码

?

五、参考文章

http://www.cnblogs.com/zabery/archive/2011/07/26/2117103.html
http://wxg6203.iteye.com/blog/668968
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  堆排序