算法导论之二项堆

每件事物都有其应需而生的目的,既然存在了,一定有其出现的因和果。二项堆的存在,就是因为二叉堆在Union操作上性能不如意而被发明的。二项堆的Union操作只需O(lgn)时间就可以完成两个二项堆的合并(总共n个元素)。

二叉堆是完全二元树(二叉树)或者是近似完全二元树(二叉树)。二叉堆有两种:最大堆和最小堆。最大堆:父结点的键值总是大于或等于任何一个子节点的键值;最小堆:父结点的键值总是小于或等于任何一个子节点的键值。

那么二项堆的定义呢?二项堆是由一组二项树组成,所以先看二项树定义。

二项树Bk是一种递归定义的有序树。二项树B0只包含一个节点。二项树Bk由两颗树Bk-1连接而成:其中一颗树的根式另一个树的根的最左孩子。二项树Bk具有以下性质:

1)共有2k个节点

2)树的高度为k

3)在深度i处恰有个节点,其中i=0,1,…,k

4)根的度数为k,大于任何其他结点的度数,并且如果根的子女从左到右编号为k-1,k-2,…,1,0,子女i是子树的Bi根。

二项堆H是由一组二项树组成,并满足以下性质:

1)H中的每个二项树都遵循最小堆性质:结点的关键字大于或等于其父节点的关键字;

2)对任意非负整数k,在H中至多有一棵二项树的根具有度数k。

这两点总结来说,关系到二项堆中二项树的两点,一是结点度数,另一个是节点关键字值。首先关键字值要有序,父结点要小于子结点,其次,二项堆中二项树的根结点度数要互异(不能有两棵二项树的根的度数是一样)。这是在二项堆操作中一定要守恒的。二项堆中结点的域有:指向父节点的指针p[x]、指向最左孩子的指针child[x]、指向紧右兄弟的指针sibling[x]、结点度数degree[x]、关键字值key[x]。

二项堆操作,如创建、查找最小关键字、删除关键字等都要维持其性质,其中合并两个二项堆需要重点说明其算法和性能。

Fun_Binominal_heap_Union(H1,H2){

    Make_Binominal_Heap(H);//建一个新堆

    head[H]=Binominal_Heap_Merge(H1,H2);//合并成一个按度数递增次序排列的链表,堆的根按照度数递增作为新堆的根

    if head[H]=null then return H;

    prev[x]=null;

    x=head[H];

    next[x]=sibling[x];//第一棵树根作为第一个结点

    while next[x] is not null

        do if (degree[x] ≠degree[next[x]]

or(sibling[next[x]] is not null and degree[x]=dgree[sibling[next[x]]]

thenprev[x]=x 

     x=next[x]

            else if key[x] =< key[next[x]]

                 thensibling[x]=sibling[next[x]]

                      Binoial_link(next[x],x);

            else if prev[x]=null

                then head[H]=next[x]

                else sibling[prev[x]]=next[x]

                     Binoial_link(x,next[x]);

                     x=next[x];

            next[x]=sibling[x];

        return H;

}

函数内,主要是对合并后结点做处理,以确保满足二项堆和二项树性质,可以看到主要对结点的比较就是度数和关键字值。看下两个度数相同的二项树Bk-1合并成一颗树Bk的函数。

Fun_ Binoial_link(x,y){

    p[y]=x; //x作为y的父结点

    sibling[y]=child[x];//x的孩子成为y的兄弟

    child[x]=y;

    degree[x]=degree[x]+1;

}

Binominal_heap_Union函数运行时间是O(lgn),其中n是二项堆H1(n1)和H2(n2)中总的结点数。H1至多包含lgn1+1个根,H2至多包含lgn2+1个根,合并后H至多包含2lgn+2,即O(lgn)个根。而执行函数的时间主要是在根结点循环,所以总共至多执行lgn2+2次迭代。

比较有趣的一个操作是当关键字值减少时调整二项堆使其保持性质,也是需要O(lgn)运行时间,要去判断关键字值。那么能否先删除关键字再作为新的关键字插入呢?新关键字插入导论中没有给出,但我想可以作为堆合并来处理,因为一个结点就是一颗二项树或二项堆,所以时间也是O(lgn),但删除关键字的时间也需要O(lgn),总共需要O(2lgn)次,所以还是直接在原来结点调整值,然后保持二项堆性质。

虽然理解了定义和基本操作,但二项堆还没想到具体应用点。

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