堆排序(heap-sort)、基于堆排序的优先队列;

堆是一种数据结构,是一个数组,它可以看成一个近似的完全二叉树。树上的每一个节点对应一个元素。
数组A包括两个属性:A.length给出数组元素的个数,A.heap-size有多少个堆元素存在该数组中;
在A[1.....A.heap-size]存放的是有效数组元素;


下面我们来分析某个节点,我们找出他的父节点,及其左右孩子节点的坐标:

我们现在讨论一下最大堆于最小堆;

在最大堆中除了根节点以外所有的节点i都要满足下面的条件:

A[parent(i)]>=A[i];

而在最小堆中除了根节点以外所有的节点i都要满足下列条件:

A[parent(i)]<=A[i];


节点中的高度与树中的高度有点不一样,来看定义吧:

该节点到叶节点最长简单路径上边的数目,我们称这个数为该节点的高度;


至于堆排序我们可以分解成下列几个子程序:

  1. 维护堆的性质:
  2. 建堆:
  3. 堆排序:

我们先来看维护堆的性质:


上面的代码说明了:某一节点如果比他的孩子要小,他们则互换位置,接着在判断该节点互换位置之后,与新的左右孩子的值相比较,如果小于则互换,继续下一步,如果大于则停止;

我们来分析这个算法的时间复杂度:这个算法主要的消耗在于递归。

我们来看看最坏的情况,最坏的情况是在树低层半满的时候:




下面我们来实现建堆着一过程:我们在维护堆性质这一过程可以看做仅仅让一个节点保证堆的性质,建堆是让全部点都满足;

我们从堆的高度为2,即倒数第二层开始从最大节点依次使用max-heapify:



我们来分析这个算法的复杂度:

这个算法复杂度主要是由循环和调用max_heapify()产生,我们需要O(n)的调用max_heapify()所以建堆这这过程的时间复杂度为:O(n lgn);

我们可以近一步分析,可以得到一个更为紧致的界,由于每个高度为h的堆的节点,调用max_heapify()的时间复杂度为O(h),我们全部节点累加则有:



下面是堆排序的过程:在前面两个过程中我们已经建立好了的以堆;我们采用如下思想来排序:

首先我们知道根节点是保存着最大值的,我们让最后一个节点与根节点互换位置,这样我们可以把最大值拉出来,互换之后,很可能是不满足堆的性质,这时候我们再对换都根节点的节点使用 max_heapify(),这样有是一个堆;这样堆的节点减一,一次这样我们便能完成排序:


时间复杂度为:O(n)+O(n lg n)=O(n lg n);


优先队列:

优先队列(priority queue)是一种用来维护一组元素构成的集合S的数据结构,其中的每一个元素都有一个相关的值,称为关键字(key)

一个最大优先队列支持如下操作:

insert(S,x):将元素x插入集合中;

maximum(S):返回S中具有最大关键字的元素;

extract-max(S):去掉并返回S中的具有最大关键字的元素;

increase-key(S,x,k):将元素的值增加到k,这里假设k的值要大于x;


现在我们来讨论最大优先队列:

1.可以再O(1)的时间内完成maximum操作:



2.heap-extract-max实现extract-max操作:


时间复杂度为:lg n;


3.heap_increase_key实现increase_key:


具体算法思想如下:将k值赋值给A[i],一般来说此时可能不满足最大堆的性质,而维护堆的性质是由上至下的,而此时的i不是在最上层,所以只能将其与其的父节点比较...若大于父节点,在于上一层比较,小于则停止,否则循环到根节点处循环结束;时间复杂度为lg n;


4.max_heap_insert实现insert操作:


算法思想如下:

首先我们将A的规模加1,然后将新增的节点初始化为一个较小的数,然后调用上面那个函数,将这个值增加到我们想要插入的值的大小,然后该函数将排序好。

时间复杂度和上一个函数一样,为lg n;







阅读更多
个人分类: 算法
想对作者说点什么? 我来说一句

没有更多推荐了,返回首页

关闭
关闭
关闭