算法导论之斐波那契堆

斐波那契堆,和二项堆类似,也是由一组最小堆有序的树构成。注意区别,不是二项树,是有根而无序的树。导论中,斐波那契堆只是具有理论上的意义,是以平摊分析为指导思想来设计的数据结构,主要是渐进时间界比二项堆有改善。斐波那契堆除去删除元素操作外,其他操作只有O(1)的平摊运行时间,而二项堆需要O(lgn)的最坏情况运行时间。但若要斐波那契堆能转化为实际应用,除要保证有相同平摊时间界限外,还需更简单的数据结构。导论中提到斐波那契堆应用于最小生成树和寻找单源最短路径,可重点理解。

总结来说,斐波那契堆,相对二项堆来说,在多数操作上改善渐进时间,但仅适用于删除操作动作较少的场景,且其结构复杂实用价值不大。下面看看斐波那契堆的结构及各操作的平摊分析势能方法。

斐波那契堆的结点,包含指向父结点的指针p[x]、指向任一子女的指针child[x]、左兄弟指针left[x]、右兄弟指针right[x]、结点子女个数degree[x]、标记是否失去子女mark[x]。值得一提的是,通过左右兄弟指针,子女结点被链接成一个环形双链表。

对给定的斐波那契堆H,通过指向包含最小关键字的树根的指向min[H]来访问,这个结点就是斐波那契堆中的最小结点。所有的树根通过left和right指针构成一个环形双链表,为堆的根表。指针min[H]指向根表中具有最小关键字的结点。H中所包含的结点个数为n[H]。斐波那契堆用势能方法分析其操作的平摊性能,对给定的斐波那契堆H,t(H)表示H根表中树的棵树,m(H)表示H中有标记结点的个数,势定义如下:


斐波那契堆的操作要分两类,一类是建堆、插入结点、合并堆和抽取堆最小值;另一类是关键字值减少和结点删除。

1)第一类操作斐波那契堆是一组无序的二项树,符合二项树的性质,性质四有所不同,描述为:无序二项树Uk,根的度数是k,大于任何其他结点的度数;根的子女按照某种顺序分别为U0,U1,…,Uk-1的根。n个结点的斐波那契堆由一组无序的二项树构成。基本操作的平摊性能都是O(1),抽取最小值是O(lgn)。

2)第二类操作斐波那契堆是一组无序的树,即不保持二项树性质,操作的平摊性能是O(lgn)。

建堆、插入结点(直接作为一颗二项树放在斐波那契堆的根表,然后将最小关键字指针重新调整)、寻找最小结点(第一个结点就是)、合并两个斐波那契堆(简单将两个堆的根表合并即可),都是O(1)代价。重点要说明抽取最小结点的算法,和第二类中关键字值减小和删除结点操作,都是O(lgn)代价。

1)抽取最小结点

抽取最小结点的处理逻辑很直接:将最小结点的子女都成为一个根,然后删除最小结点,接着将度数相同的根链接起来,调整到每个度数只有一个根为止。具体算法如下:

Fun_Fib_Heap_Extract_Min(H){

    z=min[H];

    if z ≠ null

        then for each child x of z

            do add x to root list of H

            p[x]=null

        remove z from the root list of H   //将最小结点的子女调整为根并删除最小结点

    if z=right[z] then min[H]=null

    else min[H] = right[z]  //将右兄弟作为最小结点,就是堆的入口结点

         consolidate(H) //调整堆

    n[H]=n[H]-1 //堆结点数减1

    returun z

}

重点是堆调整,使根结点的度数都是唯一的,且保持无序二项树性质。

Fun_ consolidate(H){

    for i=0 to D(n[H]) //结点度数上界

        do A[i]=null

    for each node w in the root list of H

        do x=w

           d=degree[x]

           while A[d] ≠ null

               do y=A[d]

               if key[x]

               remove y form the root list of H

               make y a child of x,incrementingdegree[x]

               mark[y]=false

               A[d]=null

               d=d+1

           A[d]=x //对根表中的每一个根w进行处理,使每个度数下至多只有一个根(发现有相同度数,小值作为根,大值下沉为子女),且数组A指向每一个留下的根。用A数组作为辅助数组,将根表中每个结点的度数映射过去。要对y进行标记,是势函数计算依据。这种处理思路就使用利用结点度数0-n的最大值作为数组来存储根表中结点的度数,是一种很有用的常量映射办法。

    min[H]=null

    for i=0 to D(n[H])

        do if A[i] ≠ null

            then add A[i] to the root list of H

                if min[H]=null orkey[A[i]]

                    then min[H]=A[i]

}

2)关键字值减小

斐波那契堆结点关键字值减小,主要是保持最小堆有序的性质,具体算法如下:

Fun_Fib_Heap_DecreaseKey(H,x,k){//x结点关键值减少为k

    if k>key[x] then return;

    key[x]=k

    y=p[x]

    if y ≠ null and key[x]

        then CUT(H,x,y)

             Cascading-cut(H,y)

   If key[x]

}

如果x结点减小后的关键值小于父结点y,则要调整堆,具体算法如下:

Fun_CUT(H,x,y){

    remove x from the child list ofy,decrementing degree[y]

    add x to the root list of H

    p[x] =null

    mark[x]=false

}

将x链接到根表。

Fun_ Cascading-cut(H,y){

    z=p[y]

    if z ≠null

        then if mark[y]=false //失去一个子结点

            then mark[y]=true

            else CUT(H,y,z)

                 Cascading-cut(H,z)

}

级联处理,保持最小堆性质。删除结点就可以通过减少结点关键值和抽取最小结点来完成,先把要删除的结点关键值减少为负无穷大,然后抽取最小值结点来作为堆入口。

算法导论之斐波那契堆_第1张图片

你可能感兴趣的:(Algorithm,算法导论专栏)