斐波那契堆

斐波那契堆同二项堆一样,也是一种可合并堆。相对于二项堆,斐波那契堆的优势在于如果不涉及删除元素的操作,则它的平摊运行时间为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. 删除最小关键字节点

  • 1
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
斐波契堆是一种数据结构,用于实现优先级队列。下面是一个简单的斐波契堆的 Java 实现: ```java import java.util.*; public class FibonacciHeap<T> { private Node<T> maxNode; private int size; // 节点定义 private static class Node<T> { T key; int degree; boolean marked; Node<T> parent; Node<T> child; Node<T> left; Node<T> right; public Node(T key) { this.key = key; left = this; right = this; } } // 插入节点 public void insert(T key) { Node<T> node = new Node<>(key); if (maxNode != null) { node.left = maxNode; node.right = maxNode.right; maxNode.right = node; node.right.left = node; if (compare(node, maxNode) > 0) { maxNode = node; } } else { maxNode = node; } size++; } // 合并两个斐波契堆 private void merge(FibonacciHeap<T> other) { if (other.maxNode == null) { return; } if (maxNode == null) { maxNode = other.maxNode; size = other.size; return; } Node<T> leftNode = maxNode.left; Node<T> rightNode = other.maxNode.right; maxNode.left = rightNode; rightNode.left = leftNode; leftNode.right = rightNode; rightNode.right = maxNode; if (compare(other.maxNode, maxNode) > 0) { maxNode = other.maxNode; } size += other.size; } // 提取最大节点 public T extractMax() { Node<T> max = maxNode; if (max != null) { // 将最大节点的子节点移动到根列表中 Node<T> child = max.child; while (child != null) { Node<T> nextChild = child.right; child.left = maxNode; child.right = maxNode.right; maxNode.right = child; child.right.left = child; child.parent = null; child = nextChild; } // 从根列表中移除最大节点 max.left.right = max.right; max.right.left = max.left; if (max == max.right) { maxNode = null; } else { maxNode = max.right; consolidate(); } size--; return max.key; } return null; } // 合并度数相同的树 private void consolidate() { int maxSize = (int) Math.floor(Math.log(size) / Math.log(2)) + 1; List<Node<T>> degreeTable = new ArrayList<>(maxSize); for (int i = 0; i < maxSize; i++) { degreeTable.add(null); } List<Node<T>> roots = new ArrayList<>(); Node<T> current = maxNode; Node<T> start = maxNode; do { roots.add(current); current = current.right; } while (current != start); for (Node<T> root : roots) { Node<T> node = root; int degree = node.degree; while (degreeTable.get(degree) != null) { Node<T> other = degreeTable.get(degree); if (compare(node, other) < 0) { Node<T> temp = node; node = other; other = temp; } link(other, node); degreeTable.set(degree, null); degree++; } degreeTable.set(degree, node); } maxNode = null; for (Node<T> root : roots) { if (root != null) { if (maxNode == null) { maxNode = root; } else { if (compare(root, maxNode) > 0) { maxNode = root; } } } } } // 连接两个节点 private void link(Node<T> child, Node<T> parent) { child.left.right = child.right; child.right.left = child.left; child.parent = parent; if (parent.child == null) { parent.child = child; child.right = child; child.left = child; } else { child.left = parent.child; child.right = parent.child.right; parent.child.right = child; child.right.left = child; } parent.degree++; child.marked = false; } // 比较节点 private int compare(Node<T> x, Node<T> y) { return ((Comparable<T>) x.key).compareTo(y.key); } // 获取堆的大小 public int getSize() { return size; } } ```
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值