您的位置:首页 > 其它

算法导论学习笔记-第二十章-斐波那契堆

2010-08-09 14:29 513 查看
第二十章 斐波那契堆

总结:这一章讲了斐波那契堆,它是一种比二项堆更松散的堆,它由一组无序的二项树组成,对不涉及删除元素的操作,它仅需O(1)的平摊运行时间。本章介绍斐波那契堆的插入、合并、删除等操作。

1. 斐波那契堆的结构
每个结点x的域:
1) 父节点p[x]
2) 指向任一子女的指针child[x]
3) 左兄弟left[x]
4) 右兄弟right[x]
5) 子女的个数degree[x]
6) 布尔值域mark[x]

在斐波那契堆中,结点x的子女被链接成一个环形双链表,称为x的子女表。当left[x]=right[x]=x时,说明x是独子。

mark域记录了每个结点的一小段历史:
1) 在某个时刻,x是个根
2) 然后,x被链接到另一个结点上(CONSOLIDATE (FIB-HEAP-LINK) )
3) 再通过切断来去除x的两个子女(CUT, CASCADING-CUT)
一旦第二个孩子失掉了,x与其父节点之间的联系就被切断了,x成为一个新根。如果发生了第1步和第2步,且x的一个孩子被切割掉了,则mark[x]为TRUE。

在一个斐波那契堆中,所有树的根都通过用其left和right指针链接成一个环形的双链表,称为该堆的根表。min[H]指向根表中具有最小关键字的结点。n[H]代表H中目前包含的结点个数。

势函数:Φ(H) = t(H) + 2m(H),其中t(H)为根表中树的棵树,m(H)为H中有标记的结点的个数。

最大度数:结点最大度数的上界D(n)=O(lgn)

2. 可合并堆的操作
1)创建
MAKE-FIB-HEAP,分配并返回一个斐波那契堆对象H,且n[H]=0, min[H]=NIL。
实际代价:O(1)
势:Φ(H)=0
平摊代价:O(1)

2)插入
插入操作直接将结点插入根表中。
实际代价:O(1)
势的增加:(t(H)+1+2m(H))-(t(H)+2m(H))=1
平摊代价:O(1)+1=O(1)

伪代码

FIB-HEAP-INSERT(H,x)

degree[x] <- 0
p[x] <- NIL
child[x] <- NIL
left[x] <- x
right[x] <- x
mark[x] <- FALSE
concatenate the root list containing x with root list H
if min[H]=NIL or key[min[H]] > key[x]
then min[H] <- x
n[H] <- n[H]+1

3)寻找最小结点
min[H]指向最小结点
实际代价:O(1)
势的增加:0
平摊代价:O(1)

4)合并
直接将两个堆的根表并置,并确定新的min[H]
实际代价:O(1)
势的增加:0
平摊代价:O(1)

伪代码

FIB-HEAP-UNION(H1,H2)

H <- MAKE-FIB-HEAP()
min[H] <- min[H1]
concatenate the root list of H2 with the root list of H
if(min[H]=NIL or key[min[H1]]>key[min[H2]])
then min[H] <- min[H2]
n[H] <- n[H1]+n[H2]
free the objects H1 and H2
return H

5)抽取最小结点
先将最小结点的每个子女都成为一个根,然后将度数相同的根链接起来。注意,直到这里,才真正完成了根表中的树的合并。
实际代价:O(D(n)+t(H))
势的增加:至多(D(n)+1+2m(H))-(t(H)+2m(H))=D(n)-t(H)+1
平摊代价:O(D(n))=O(lgn)

伪代码

FIB-HEAP-EXTRACT-MIN(H)

z <- min[H]
if z!=NIL
then for each child x of z
do add x to the root list of H
p[x] <- NIL
remove z from the root list of H
if z=right[z]
then min[H] <- NIL
else min[H] <- right[z]
CONSOLIDATE(H)
n[H] <- n[H]-1
return z

CONSOLIDATE(H)将H根表中的度数相同的结点链接起来,直到根表中每个度数至多只有一个根。用到辅助数组A[0…D(n[H])],A[i]=y代表degree[y]=i

伪代码

CONSOLIDATE(H)

for i <- 0 to D(n[H])
do A[i] <- NIL
for each node w in the root list of H
do x <- w
d <- degree[x]
while A[d]!=NIL
do y <- A[d]
if key[y]<key[x]
then exchange y <-> x
FIB-HEAP-LINK(H,y,x)
A[d] <- NIL
d <- d+1
A[d] <- x
min[H] <- NIL
for i <- 0 to D(n[H])
do if A[i]!=NIL
then add A[i] to the root list of H
if min[H]=NIL or key[A[i]]<key[min[H]]
then min[H] <- A[i]

伪代码

FIB-HEAP-LINK(H,y,x)

remove y from the root list of H
make y a child of x, incrementing degree[x]
mark[y] <- FALSE

6)减小一个关键字
若结点的关键字减小后,关键字小于其父节点的关键字,则将此结点移出,置入根表中,另外,还需执行级联切断操作。

伪代码

FIB-HEAP-DECREASE-KEY(H,x,k)

if k > key[x]
then error “error”
key[x] <- k
y <- p[x]
if y!=NIL and key[x]<key[y]
then CUT(H,x,y)
CASCADING-CUT(H,y)
if key[x]<key[min[H]]
then min[H] <- x

伪代码

CUT(H,x,y)

remove x from the child list of y, decrementing degree[y]
add x to the root list of H
p[x] <- NIL
mark[x] <- FALSE

由于CUT使切断的结点的父节点y又失去了一个孩子,因此CASCADING-CUT判断父节点的MARK,若为FALSE,则由于y失去了一个孩子,设mark[y]为TRUE,若mark[y]已经为TRUE了,那么y已经失去了两个孩子了,那么继续切断y与p[y],再接着判断p[y]的MARK,一直沿树递归上去,直到找到一个根节点或未加标记的结点。

伪代码

CASCADING(H,y)

z <- p[y]
if z!=NIL
then if mark[y]=FALSE
then mark[y]=TRUE
else CUT(H,y,z)
CASCADING-CUT(H,z)

复杂度分析:
设递归调用了c次CASCADING()
实际代价:O(c)
势的变化:至多(t(H)+c+2(m(H)-c+2))-(t(H)+2m(H))=4-c (c-1个结点被级联切断消除标记,最后一次CASCADING-CUT可能给某个结点加上标记)
平摊代价:O(c)+4-c=O(1)

7)删除
平摊代价:O(lgn)

伪代码

FIB-HEAP-DELETE(H,x)

FIB-HEAP-DECREASE-KEY(H,x,-INF)
FIB-HEAP-EXTRACT-MIN(H)
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: