📅  最后修改于: 2023-12-03 15:26:13.750000             🧑  作者: Mango
斐波那契堆
斐波那契堆 (Fibonacci Heap) 是一种优化了的二叉堆数据结构。与二叉堆一样,它也拥有 $O(\log n)$ 的插入和删除最小元素的时间复杂度。相比之下,它的合并(meld)操作和删除任意元素的时间复杂度要更低,分别为 $O(1)$ 和 $O(\log n)$。这使得它特别适合于那些需要频繁地进行合并和删除操作的场景。例如最短路算法中的 Dijkstra 算法以及生成树算法中的 Prim 算法等。
斐波那契堆由 Michael Fredman 和 Robert Tarjan 于 1984 年首次提出,它的削减(decrease key)和合并(meld)操作是一些重要的优化技巧的生动体现,被认为是算法设计和分析中的经典案例之一。
数据结构
斐波那契堆是一个由具有某些特殊性质的树所组成的森林。每棵树都遵循斐波那契堆的性质:任何非根节点 $x$ 的关键字都不大于其父节点 $p(x)$ 的关键字。而且,每棵树的根节点都保存了一个指向任意子节点 $y$,且 $y.key$ 最小的指针。这使得最小元素可以在森林中被快速地找到。
斐波那契堆的每个节点都包含以下几个属性:
- $key$:节点的关键字,用于确定节点的顺序。
- $degree$:节点的度数,即它拥有的子节点数。
- $parent$:节点的父节点。
- $child$:节点的第一个子节点。
- $left$:节点的左兄弟。
- $right$:节点的右兄弟。
- $marked$:指示节点是否已经失去过一个子节点。
其中,关键字、左兄弟和右兄弟可以被用于将相同度数的节点链接成一个双向循环链表。
斐波那契堆还维护了一个指向最小元素的指针 $min$,可以在 $O(1)$ 的时间内访问。
操作
斐波那契堆支持以下几种主要操作:
- MAKE-HEAP():创建一个斐波那契堆。
- INSERT(H, x):在堆 $H$ 中插入节点 $x$。
- MINIMUM(H):返回堆 $H$ 中的最小节点。
- EXTRACT-MIN(H):删除并返回堆 $H$ 中的最小节点。
- UNION(H1, H2):将堆 $H2$ 合并到堆 $H1$ 中。
- DECREASE-KEY(H, x, k):将节点 $x$ 的关键字减少到 $k$。
- DELETE(H, x):删除节点 $x$。
其中,MAKE-HEAP() 和 MINIMUM(H) 操作的时间复杂度均为 $O(1)$。INSERT(H, x) 和 UNION(H1, H2) 操作的时间复杂度均为 $O(1)$。EXTRACT-MIN(H)、DECREASE-KEY(H, x, k) 和 DELETE(H, x) 操作的时间复杂度均为 $O(\log n)$。
几个重要的优化
- 延迟合并:斐波那契堆在节点删除的过程中需要进行大量的子节点合并操作。为了减少这些操作的次数,可以采用延迟合并(lazy meld)的方式。具体来说,堆删除操作不会立即合并两个相同度数的根节点,而是等到之后的某个节点合并操作时再进行合并,这样就可以减少大量的合并操作。
- 重新连接:在斐波那契堆的节点删除操作中,当一个节点失去了一个子节点时,会将其和其他具有相同度数的节点分别放到单独的根列表中。这个过程可能会使得根列表中的节点数量非常多,影响堆性能。为了减少这个问题,可以采用重新连接的方式。具体来说,当一个节点失去子节点后,会将其从根列表中删除,并将其子节点插入到根列表中,然后对新的根列表进行一次重新连接操作,这个过程会使得根列表中的节点数量减少到 $O(\log n)$。
- 多-pass 算法:斐波那契堆中删除任意节点的时间复杂度为 $O(\log n)$。这主要是由于节点删除操作可能会引起大量的子节点合并操作。为了减少这些操作的次数,可以采用多-pass 算法。具体来说,每次节点删除操作时,会首先将节点标记为已删除,并将其从堆中删除。然后会将节点的父节点和跟节点分别进行合并操作,直到堆中不存在被标记的节点为止,这样就可以减少大量的子节点合并操作。