斐波那契堆

来源:互联网 发布:怎么使用百度的数据库 编辑:程序博客网 时间:2024/05/04 07:43

斐波那契堆同二项堆一样,也是一种可合并堆。相对于二项堆,斐波那契堆的优势在于如果不涉及删除元素的操作,则它的平摊运行时间为O(1)。但是由于其算法过于复杂, 因而实际上更多的是用二项堆。

一、定义

一个斐波那契堆具有如下性质:

  1. 堆有一组有序树组成,但是堆中的树不一定是二项树
  2. 斐波那契堆中的树之间是无序的(二项堆中的树是按照其包含的二项树的度数排序的)
  3. 堆中每个节点的数据结构包含如下域:
    1. 指向其父节点的指针域
    2. 指向其任意一个孩子的指针域
    3. 任意一个节点x的所有孩子被链接在一个双向链表环形链表中
    4. 节点x中保存有它的孩子的数目degree
    5. 节点x中存在一个域mark,用于表示自从x上一次称为另一个节点的子女以来,它是否失掉了一个孩子,TRUE表示是失去了,FALSE表示未失去
  4. 斐波那契堆中的所有树的树根被保存在一个链表即根表中
  5. 对于一个斐波那契堆,min[H]保存了具有最小节点值的根节点
下图即为一个斐波那契堆的示例:

对于斐波那契堆上的各种可合并操作,关键思想是尽可能久地将工作推后,可以理解为斐波那契堆的操作是懒惰的。

二、插入新节点

插入新节点在斐波那契堆中非常简单,将新的节点当做一棵新的树的根插入到根表中即可。下图即为一个示例:

上图为往斐波那契堆中添加新的节点21的情形。

三、合并两个斐波那契堆

合并两个斐波那契堆的操作非常简单,可以分为两步:
  1. 将两个根表通过指针合并为一个根表(如果是c语言实现,只需要让其中一个根表的最后一个节点的next指向另一个堆的根表的头即可)
  2. 更新min[H],这一步也很简单,只需要比较两个堆的min[H],取较小的即可

四、删除最小节点

删除最小节点的操作比较复杂, 推迟的工作在这里都要被完成,其算法思想为:
  1. 删除最小节点,并将被删除节点的每个孩子都看做新的堆中的一棵树的根,将它们加入到根表中
  2. 遍历根表,合并度数相同的树
    1. 定义一个辅助数组A,A[i]=y表示树 y 的degree值是 i,数组的元素被初始化为NULL。
    2. 获取当前根节点所在树的degree
    3. 如果A[degree]!=NULL,则发现两棵度数相同的树,按照排序树的要求合并它们,并更新合并后所得树的degree(按照合并算法,它必然是degree较大那个值加1),执行第5步
    4. 如果A[degree]!=NULL,则更新A[degree]为当前节点
    5. 遍历下一个节点
下图即为一个示例:

上图即为删除最小节点的一个示例。

五、减小一个节点的关键字

减小一个关键字的字,会破坏最小堆的性质,所以要进行最小堆维护。因为斐波那契支持减小关键字和删除结点操作,所以斐波那契堆的子树就不一定是二项树了。
减小一个关键字的算法思想如下:
  1. 减小关键字,如果破坏最小堆性质,则将该结点a直接从原来的树移除直接串联在根表中
  2. 如果节点a的父节点p没有失去过孩子,则将父结点p的mark属性设置成true,即标记为该节点失去过孩子,然后结束,否知执行下一步
  3. 将节点p当做被删除节点a,将它从树中剪掉,移到根表中
  4. 回到第2步执行
一个示例如下图所示:

六、删除一个关键字

基于较小关键字和删除最小节点的操作,删除一个关键字的操作很简单,分为如下几步:
  1. 找到要删除的关键字
  2. 将该关键字减小为比最小值还小的值
  3. 删除最小关键字节点

原创粉丝点击