优先队列(堆)

    本文将概括性描述优先队列ADT,只对基本的堆数据结构做简要总结,不涉及左式堆,d-堆,斜堆,二项队列等高级数据结构。(算法入门级的总结,欢迎批评指正,根据《数据结构与算法(JAVA语言描述)》一书总结)

   优先队列

1.模型

优先队列是至少支持两种操作的数据结构:insert 与 deleteMin。前者等价于队列中的入队,后者等价于出队。

此模型在贪婪算法与外部排序中有较多应用。

2.二叉堆

二叉堆是优先队列的普遍实现。

堆的两个性质:结构性和堆序性。

结构性:堆是一棵被完全填满的二叉树,底层可能是例外,从左到右插入。(完全二叉树)

一棵高为h的完全二叉树有2^h到2^(h+1) - 1个节点,即完全二叉树的高是小于logN的最大整数。

因为完全二叉树的规律性,所以他可以用数组代替链来表示。数组是对二叉树广度优先遍历的结果(数组从角标1开始存值,留下0为空位方便其他操作使用)。

堆序性:即让操作快速执行的性质,即任一节点应该小于它的所有后裔。在一个堆中,对于每一个节点X,X的父亲中的关键字小于或等于X中的关键字,根节点除外。由于此性质findMin的操作花费为O(1)。

3.基本堆操作

首先所有的工作都必须满足堆序性质。

1.insert插入

    为插入一个新元素X,我们可以在下一个可使用位置创建空穴(保证该堆为完全树,即结构性),如果X可以放在该空穴中而不破坏堆序性,那么插入完成,否则就将该堆父节点的元素移入该空穴中(即空穴深度减一)继续上述判断过程直到X放入空穴中不破坏堆序性质为止。这种操作一般称之为上滤。

上滤方法代码如下

public void insert(AnyType x)
{
    //判断是否到达数组容量上限
    if(currentSize == array.length - 1)
        enlargeArray(array.length - 1);
    //上滤操作
    int hole = ++currentSize;
   for(array[0] = x;x.compareTo(array[hole/2]) < 0;hole /= 2)
    //将父节点放在空穴处,空穴上移
      array[hole] = array[hole/2];
    array[hole] = x;
}

其实如果直接将X放入空穴反复执行与父节点的插入操作也可以完成上滤操作,可是上滤一层需要交换3次。而这里的方法只需要1次,最后再加一次赋值操作即可。至于对array[0]的赋值操作是为了保证循环的终止,假设新插入元素一直为最小值,则会被上滤到根处,这样compareTo的比较结果为0,可退出循环且此时hole值应为1.

    insert操作的最坏运行时间为O(logn)平均时间花费为O(1);

2.deleteMin(删除最小元)

    其方法与插入操作有相似之处,因为要去除一个元素,所以我们现将堆中的最后一个元素X删除并记录它的值。然后在根节点处建立空穴,如果最后一个元素X可以放入空穴则deleteMin操作完成,否则比较他的两个儿子的大小,将小的儿子放到根节点处,重复这一过程直到X可以放入空穴或空穴被放入最后一层。然后将记录的值填入空穴完成deleteMin操作。

    注意:在比较两个儿子大小时要确保两个儿子的存在,需要通过附加测试来确定是否存在一个或两个儿子。这种测试只会在倒数第二层生效。(因为完全二叉树的特性)。代码省略。

deleteMin操作的最坏运行时间为O(logn)因为来自底层的元素X几乎都被过滤到底层,所以平均运行时间为O(logn)。


4.其他的堆操作

descreaseKey(降低关键字的值)

descreaseKey(p,x)操作降低在位置p处的值,幅度为x,上滤进行调整。

管理员可以通过此方法使他们的程序优先运行


increaseKey(增加关键字的值)

方法与上述增加方法作用相反,许多调度程序自动降低正在过多消耗CPU时间的进程的优先级


delete(删除)

delete(p)删除堆中位置为p的节点。该操作通过执行descreaseKey(p,无限大)然后执行deleteMin来完成。


buildHeap(构建堆)

由于insert操作平均花费O(1),buildHeap即对一个N项集合进行N次insert操作,所以buildHeap的运行时间应该为O(n)平均时间而不是O(nlogn)最坏运行时间(证明过程太过复杂,省略)。


5.优先队列的应用

1.选择问题

关于在N个元素中找出第k个最大元素的选择问题。

对于传统排序算法,该问题较为聪明的解法是:

现将k个元素读入数组并排序,这些元素的最小者在第

k个位置上,然后依次处理剩下的元素,如果大于第k个元素则将其放入数组中正确的位置并将原来的第k个元素除去。算法结束时第k个元素即为解,此方法运行时间为O(N*k)(N^2 - (N - K) * K).如果k正好为N的一半则是这个问题最复杂的解答O(N^2)。(因为任意的k可以对称)。即求中位数,正好这是我们用的最频繁的一种情况。

通过buildHeap可以将此最坏运行时间降至O(NlogN)

为简单起见,我们找出第k个小的元素,我们先构建一个堆O(n)然后执行k 次deleteMin操作,每次用时O(logn)因此此算法在k比较小的情况为O(N)最坏运行时间为O(nlogn)。

对于原问题:求第k个大的元素,我们可以先构建拥有K个元素的堆S,然后执行数次insert与deleteMin操作。。。


over


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值