Fibonacci Heaps

motivation

A Fibonacci heap can be viewed as an extension of Binomial heaps which supports DECREASE-KEY and DELETE operations efficiently.

我们为什么需要更高效的 decreaseKey?

设想在 Dijkstra单源点最短路径问题上,G=(V,E), |V| = n, |E| = m,我们使用优先队列,算法实现可看为 n 次 insert,n 次 extractMin 和 m 次decreaseKey 的 sequnce. 用 amortized cost = O(n\cdot T_{insert} + n\cdot T_{extractMin} +m\cdot T_{decreaseKey}). 我们知道无论如何优化 n 次 insert 和n 次 extractMin 是无法优于O(n\log n)的。那么就只能考虑在其他不变的情况下,优化 decreaseKey.

Fibonacci Heaps

已知 binary 和 binomial heaps 的 decreaseKey 都取决于树的高度,O(log n). 对于斐波那契堆我们希望把decreaseKey的复杂度降到O(1) —— decreaseKey 直接将该 node 从它的 parent 取下来,再加入到 set 里,其余操作均与 lazy binomial heap相同,详见lazy binomial heap.

decreaseKey

但是这样会有一些问题,因为我们无法同时保持下面三种性质:

  • decrease-key takes time O(1).
  • Our trees are heap-ordered.
  • Our trees are binomial tree.

那就放弃第三条好了,这样可以随心所欲地 cut (decreaseKey).

extractMin 变慢了?

但是这样仍然会有一些问题,插入依然是 lazy meld,但如何去extractMin 呢?像之前的情况,case (a),参照 lazy binomial heap extractMin 时 coalesce step 的方式去合并。


case a
distribute order

但现在 set 里面的 tree 不再是 binomial tree了,每一棵树 nodes 个数不再是 2^k,order 如何再去标记?类似下面这种情况,case (b),怎么合并?

case b

一种解决办法是令一棵树的 order 等于 其 root 的 rank,即 root 有几个孩子。然后同 coalesce step 中按 order 分配再从低到高合并。

distribute order

coalesce.gif

但是其实这样的做法会出现很严重的问题。就是我们的树的形状不再 well-constrained. Nodes 个数 不再与这棵树的 order 成 exponential 关系。heap 里可能会出现这样形状的树:

bad-constrained

因为我们知道在对于 lazy binomial heap 的 extractMin 时进行 均摊复杂度分析时,O(\log n)是依赖于最终合并后 binomial tree n 个 nodes 最多有 O(\log n)棵树的。而此时,树的个数变成了 O(n^{\frac{1} {2}}). 这就很让人头大...O(1)的decreaseKey 是牺牲了 extractMin, O(n^{\frac{1} {2}})为代价的。

Solution

解决的思路是增加限制重新使 nodes exponential 于 tree 的 rank.

Solution: Limit #cuts among the children of any node to 2.

依然用 root 的孩子数作为 rank,但同时限制任何一个 node 最多只能失去 一个孩子。当其失去第二个孩子时,它也同时被从它的父节点取下来,从而可能造成一连串的级联切割——cascading cut. 实现时,所有节点初始均未标记。当一节点失去一个孩子时,将其标记。当其失去第二个孩子时,同时将该节点取下,并解除其标记加回到 set 里。

下面用两种方法证明这种方法构造的树 nodes exponential 于 tree 的 rank.

数理证明

整体思路是证明上面 solution 所构造 order 为 k 的 tree,所包含的节点数为斐波那契数 f_{k+2}.
首先回顾 Fibonacci 数列:
f_n=\begin{cases} 0& n=0,\\ 1 & n=1,\\ f_{n-1}+f_{n-2} & n\geq2. \end{cases}

其特征方程 z^2-z-1 = 0 有两特征根 \phi = \frac{1+\sqrt{5}} {2}\hat{\phi} = \frac{1-\sqrt{5}} {2}. 通项公式 f_n = \frac{1}{\sqrt{5}}(\phi^n+\hat{\phi}^n)

Lemma 1. For all integers n \geq0, f_{n+2} = 1+ \sum_{i=0}^{n}{f_i}
Proof by induction on n.
Base case. n = 0, f_2 = f_1+f_0 = 1 成立。
Induction Hypothesis: f_{k+2} = 1+\sum_{i=0}^k{f_i} for 0\leq k\leq n-1
To prove it holds for k = n.
f_{n+2} = f_{n+1}+f_{n} =1+\sum_{i=0}^{n-1}{f_i} + f_n=1+\sum_{i=0}^{n}{f_i}, 成立。

然后是尝试建立指数关系。
Lemma 2. For all integers n \geq0, f_{n+2}\geq \phi^n
Proof by induction on n.
Base case. f_2 = 1 = \phi^0, f_3 = 2 \geq \phi^1
Induction Hypothesis: f_{k+2} \geq \phi^k for 0\leq k\leq n-1
To prove it holds for k = n.
f_{n+2} = f_{n+1}+f_{n} \geq \phi^{n-1}+\phi^{n-2} = (\phi+1)\cdot \phi^{n-2} = \phi^2\cdot \phi^{n-2} = \phi^n, 成立。

然后是任何一个树内 node 的 rank 的限制。
Lemma 3. :Let x be any node in a Fibonacci heap, and suppose that k = rank(x). Let y_1, y_2,..., y_k be the children of x in the order in which they were linked to x, from the earliest to the latest. Then rank(y_i) \geq \max(0,i-2).

Lemma 3

Proof. 显然 rank(y_1)=0 成立。
对于i>1的情况,当y_i 被 linked 到 x时(与x合并),y_1, y_2,..., y_{i-1}已经 linked 到 x了,所以此时 rank(x) = i-1。那么因为只有对于同秩的 tree 才会合并,所以此时rank(y_i) = rank(x) = i-1.
因为此后 y_i仍属于x的孩子,所以其最多损失一个孩子,rank(y_i)\geq i-2, 证毕。

最后用前面的引理1, 2 和 3结合来证明 rank 与 节点数的关系。
Lemma 4. Let z be any node in a Fibonacci heap with n = size(z) and r = rank(z). Then r = \log_\phi n.
Proof.
假设S_k是斐波那契堆里任意 rank 为k的节点,以其为根的树所包含的节点个数(size).
显然 S_0 = 1, S_1 = 2. 它们都不能失去任何孩子。
且对二者增加孩子,以构成S_2, S_3...只会使 nodes 个数增加,size 随 order 增加不会减少。
x是斐波那契堆里任意一点,其秩r = rank(x), 节点个数S_r = size(x)

使用Lemma 3的假设和结论。令x的孩子,按被 linked 的顺序为y_1, y_2,..., y_k.
那么就有 S_r \geq 1+\sum_{i=1}^rS_{rank(y_i)} \geq 1+\sum_{i=1}^rS_{\max(0,i-2)} = 2 + \sum_{i=2}^rS_{i-2}

接下来证明S_r \geq f_{r+2},对于所有r\geq 0。即 rank = r 的 tree 的 size, S_r 大于等于斐波那契数,f_{r+2}。然后依据Lemma 2,由于f_{r+2}\geq \phi^r,所以 size 和 order(rank) 呈指数关系。

Proof by induction on n.
Base case. S_0 = 1 = f_2, S_1 = 2 = f_3,成立。
Induction Hypothesis. It holds for 0\leq k \leq r-1 that S_k \geq f_{k+2}.
To prove it holds for k = r. 使用S_r \geq 2 + \sum_{i=2}^rS_{i-2}就可以用 Induction Hypothesis 了。
S_r \geq 2+\sum_{i=2}^rS_{i-2} \geq 2+\sum_{i=2}^rf_i = 1+\sum_{i=1}^rf_i = f_{r+2}.

由此,我们推出了 n \geq S_r \geq f_{r+2} \geq \phi^r\implies r = \log_\phi n,nodes exponential 于 rank。

Maximally-Damaged Trees

上面那是石溪教材上的数理证明,下面是大S给出的更直观的证明。

Maximally-Damaged Trees

就是说我们对于一个 full 的 rank = k 的 binomial tree 去尝试截掉它所有不影响其 rank 的节点。这样就得到了斐波那契堆里 rank 为 k 的树的最少节点数——此时 tree 是 maximally damaged.

然后我们通过观察发现,这这些 minimum number 是 斐波那契数列—— order + 1 的 maximally damaged tree 就是 order - 2 的 tree 被 linked 到 order -1 的 tree上。而由数理归纳可知斐波那契数的增长是指数级的,所以\downarrow

claim: The minimum number of nodes in a tree of order k is F_{k+2}. 殊途同归。

Proof by induction on rank.

Base case & IH

Induction Hypothesis. for , order 为 的树最少含有节点

To prove it holds for k=n.
order =k 的树的孩子的 order 为0,0,1,...,k-2,这是由 maximally damaged trees 而来性质而来,它合并了 order = k-1k-2 的maximally damaged tree.
而 order = k+1 的树,则由 order = kk-1 合并,如图。

根据 IH, order = k-1 的树节点数\geq F_{k+1}, order = k树节点数\geq F_{k+2},则二者合并 order = k+1 的树节点数=F_{k+1}+F_{k+2} = F_{k+3}, it holds for k+1.

induction step

Amortized analysis

首先是 decreaseKey 的复杂度。尽管我们希望能把它优化到O(1),可以自从限制了 cut 次数以后,就可能出现级联 cut 的情况。如图:

cascading cut

这样worst-case 的复杂度就变成了O(\log n),一串 cut 取决于树高。 下面使用 potential method 来看一下 decreaseKey 的均摊复杂度。

先观察,对于k次级联 cut 的 decreaseKey 会使 tree 的个数增加k,不能再令 \Phi = # of trees。而在 trees 增加的同时,每一次级联 cut 都会还原一个 marked node。不妨令\Phi = t + 2m, t是 trees 的个数,m是 marked nodes 的个数。

这里之所以采用t+2m,是考虑t+m的话,减少的 mark nodes 知识 刚刚好抵消掉 tree的增加,而还要对 actual cost 进行抵消,所以考虑用\Phi = t+2m.

假设某次 decreaseKey 开始前,# of mark nodes = m,# of trees = t. \Phi_{before} = t+2m
操作进行了k次级联 cut。actual cost = 1+k
新增 trees 1+k, mark nodes 1-k. 因为k次级联 cut 后,必新增 1 mark node.
\Delta \Phi = 3-k, T_{amortized}(decreaseKey) = 1+k + C\cdot (3-k) = O(1)

由此可知我们确实把 decreaseKey 的均摊复杂度降到了O(1). 期待 Dijkstra的更好性能吧!

extractMin的复杂度。只分析均摊复杂度就够了,worst-case 如 lazy binomial heap。

extractMin 之前,# of mark nodes = m,# of trees = t. \Phi_{before} = t+2m

  • 通过min指针找到 min, remove min 新增 trees O(\log n),再 add back 的 actual cost O(\log n)
  • coalesce step: trees 变为 O(\log n), actual cost = O(t+\log n)
    • 先创建 order box, actual cost = O(\log n)
    • 再按 order 分配并合并。trees 变为 O(\log n), actual cost = O(t)
  • update min 指针,O(\log n).

自始至终 mark nodes 个数都不会改变。
\Delta \Phi = -t + \log n, actual cost = O(t+\log n), T_{amortized}(extractMin) = -t + \log n + O(t+\log n) = O(\log n)
分析跟 lazy binomial heap 的 extractMin 完全一致(依赖于 exponential 关系)。

delete 的复杂度。前面说斐波那契堆支持更高效的 decreaseKey 和 delete,delete的复杂度?

delete(H,x) 可以看作是 首先 decreaseKey(H,x,-\infty), 再去 extractMin(H).
所以其均摊复杂度,T_{amortized}(delete) = O(1)+O(\log n) = O(\log n)

其实没什么提升,lazy binomial heap 中 delete 也是 O(\log n) 的均摊复杂度。

where are we now

performance

拿到了这样的“神器”,Fibonacci heap 实现的优先队列来解决单源点最短路径问题能否起飞呢?

representation problem

回顾开局那张 decreaseKey 的图,其实细节上有些错误。因为要把 一个节点从其父节点的link中取下来,必然得找到父节点。那 parent 和 child 之间就必须得是双向的 link。如图:


representation

但是这样仍会有问题。试想上图中 decrease D 的 key。

  • 拿到 D 就找到了 A。找到 A 后,就先抹掉 D 指向 A 的 link,O(1).
  • 剩下只需要把 A 指向 D 的 link 删掉,D 就彻底被移除了,然而找到 A-D link O(\log n).

这就很尴尬,因为我们想要O(1)来解决这其中的所有 link.

tricky solution:

tricky solution

一个 node 的所有 children (即 siblings 之间),形成一个双向循环链表,并且这些 children 都存一个 指向其 parent 的指针。parent 随便存一个 “代表”孩子。

现在来试一下 decrease D 的 key。

首先 D 找到 A,发现 D 不是“代表”孩子,既然不代表那就该死死去。nothing changes.
D 顾左右,C-D link\rightarrowE,E-D link\rightarrowC. 把自己扣出来,铁锅炖自己,放到 A 旁边, O(1)

而如果要 decreaseKey 的是“代表”孩子,那 只需 A 的指针随便指向“代表”孩子的兄弟就好了。

小节

至此所有堆知识的进阶(graduate)都讲完了。Binary 本身已经是非常简洁高效的数据结构了。后面的 binomial heap 及其他都是在一定应用场景下诞生的更为高效的数据结构。

看起来 Fibonacci heap 无限美好。但回答之前能否起飞的问题——很大概率不能。

因为一个node 存了太多东西了:

  • A pointer to its parent.
  • A pointer to the next sibling.
  • A pointer to the previous sibling.
  • A pointer to an arbitrary child.
  • A bit for whether it's marked.
  • Its order.
  • Its key.
  • Its element

这些 constant factors 让斐波那契堆实现的优先队列在实际过程中速度不及其它的堆。除非,在一些非常非常大的图上,或者在需要 "tons of decreaseKey操作"的网络流问题上。

但理论上 Fibonacci heaps 还是值得去了解的:

  • 首先是分析时使用了 two-tiered potential method——老千层饼了。
  • 再一个是这种 decreaseKey 的思路值得去借鉴。
  • 最后是给 dijkstra 单源点最短路径 和 prim 最小生成树问题给出了理论上的 optimal 边界。
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,657评论 6 505
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,889评论 3 394
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,057评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,509评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,562评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,443评论 1 302
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,251评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,129评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,561评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,779评论 3 335
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,902评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,621评论 5 345
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,220评论 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,838评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,971评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,025评论 2 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,843评论 2 354