优先队列之 二项堆

原文链接:http://dsqiu.iteye.com/blog/1714961

1 定义

二项堆(Binomial Heap)
二项堆(Binomial Heap)是二项树(Binomial Tree)的集合(collection),所以势必要先简要介绍下二项树。关于堆(最小堆)的相关知识,作者已经在堆排序有介绍可以点击查看,这里就不多枚举了。
二项树(Binomial Tree)
二项树(Binomial Tree)是一组多分支树的序列,二项树的递归定义:
- 二项树的第0棵树只有一个结点;
- 二项树的第K棵树的子结点由二项树的前面k-1棵组成的。
- 这里写图片描述

从以上定义,不难得到下面的结论:

  • 二项树的第K棵树有 2k 个结点,高度是 k
  • 深度为 d 的结点共有(nd)个结点(从上图就可以看出结点是按二项分布的(1,3,3,1))

二项堆由一组二项树所构成,这里的二项树需要满足下列条件:
1)H中的每个二项树遵循最小堆的性质。
2)对于任意非负整数k,在H中至多有一棵二项树的根具有度数k。

对于性质2,任意高度最多有一棵二项树,这样就可以用二项树的集合唯一地表示任意大小的二项堆,比如13个结点的二项堆H,13的二进制表示为(1101),故H包含了最小堆有序二项树B3, B2和B0, 他们分别有8, 4, 2, 1个结点,即共有13个结点。如下图(另外:二项堆中各二项树的根被组织成一个链表,称之为根表)

这里写图片描述

2 实现(基于最小堆)

2.1 二项树的ADT

typedef struct BinHeapNode BinHeapNode;
typedef BinHeapNode* BinHeapNodelink;
typedef BinHeapNode* BinHeap;
struct BinHeapNode
{
    Item key;
    int degree;
    BinHeapNodelink parent;
    BinHeapNodelink leftChild;
    BinHeapNodelink sibling; //兄弟   
};

2.2 创建二项堆

BinHeap creatBinHeap()
{
    BinHeap heap = (BinHeap)malloc(sizeof(*heap));
    if(heap == NULL)
    {
        printf("Memory is not sufficient\n");
        exit(1);
    }
    return heap;
}

2.3 找最小元素

由于每一个二项树都满足最小堆的性质,所以每个二项树的最小关键字一定在根结点,故只需遍历比较根表的情况就可以。

BinHeap findBinHeapMin(BinHeap heap)
{
    if(heap == NULL)
        return NULL;
    BinHeapNodelink h = heap;
    BinHeapNodelink min_node = h;
    Item min_key = h->key;
    while((h = h->sibling) != NULL)
    {
        if(h->key > min_key)
        {
            min_node = h;
            min_key = h->key;
        }
    }
    return min_node;
}

2.4 合并两个二项堆

合并两个二项堆有三个函数:

BINOMIAL-Pair,连接操作,即将两棵根节点度数相同的二项树Bk-1连接成一棵Bk。
BINOMIAL-HEAP-MERGE ,将H1和H2的根表合并成一个按度数的单调递增次序排列的链表。
BINOMIAL-HEAP-UNION,反复连接根节点的度数相同的各二项树。

合并操作分为两个阶段:
第一阶段:执行BINOMIAL-HEAP-MERGE,将两个堆H1和H2的根表合并成一个链表H,它按度数排序成单调递增次序。MERGE的时间复杂度O(logn)。n为H1和H2的结点总数。(对于每一个度数值,可能有两个根与其对应,所以第二阶段要把这些相同的根连起来)。

第二阶段:将相等度数的根连接起来,直到每个度数至多有一个根时为止。执行过程中,合并的堆H的根表中至多出现三个根具有相同的度数。(MERGE后H中至多出现两个根具有相同的度数,但是将两个相同度数的根的二项树连接后,可能与后面的至多两棵二项树出现相同的度数的根,因此至多出现三个根具有相同的度数)

第二阶段根据当前遍历到的根表中的结点x,分四种情况考虑:

Case1:degree[x] != degree[sibling[x]]。此时,不需要做任何变化,将指针向根表后移动即可。(下图示a)
Case2:degree[x] == degree[sibling[x]] == degree[sibling[sibling[x]]]。此时,仍不做变化,将指针后移。(下图示b)
Case3 & Case4:degree[x] = degree[sibling[x]] != degree[sibling[sibling[x]]] (下图示c和d)
Case3:key[x] <= key[sibling[x]]。此时,将sibling[x]连接到x上。
Case4:key[x] > key[sibling[x]]。此时,将x连接到sibling[x]上。

复杂度:O(logn), 四个过程变化情况:
这里写图片描述

//=======================两个二项队列的连接============================
BinHeap BinHeapUnion(BinHeap H1, BinHeap H2)
{
    BinHeap heap = BinHeapMerge(H1, H2);
    if(heap == NULL)
        return NULL;
    BinHeap x, x_prev, x_next;
    x_prev = NULL;
    x = heap;
    x_next = x->sibling;
    //将相同度数的二项堆合并成一个二项堆 B_k -> B_k+1
    while(x_next != NULL)
    {
        //case 1:相邻不等,不作变化指针后移
        //case 2:连续3个相等,不作变化指针后移(下一次循环处理后2个相等的节点)
        if(x->degree != x_next->degree || (x_next->sibling!=NULL && x_next->degree == x_next->sibling->degree) )
        {
            x_prev = x;
            x = x_next;
        }

        //case 3、4:相邻2个相等,和第3个不等
        else
        {
            //case 3: 将x_next连到x
            if(x->key <= x_next->key)
            {
                x->sibling = x_next->sibling;
                BinHeapPair(x_next, x);
            }

            //case 4: 将x连到x_next
            else
            {
                if(x_prev == NULL)
                    heap = x_next;
                else
                    x_prev->sibling = x_next;

                BinHeapPair(x, x_next);
                x = x_next;
            }

        }//case 3&4
        x_next = x->sibling;

    }//while
    return heap;

}

//将2个二项队列合并成度数单调递增的一个二项队列
BinHeap BinHeapMerge(BinHeap H1, BinHeap H2)
{
    BinHeap H3 = NULL, H3_prev = NULL, heap = NULL;//heap用于指向新的二项队列, H3用作heap的索引指针
    BinHeap h1, h2;
    h1 = H1, h2 = H2;
    if(H1 != NULL && H2 != NULL)
    {
        while(h1 != NULL && h2 != NULL)
        {
            if(h1->degree < h2->degree)
            {
                H3 = h1;
                h1 = h1->sibling;
            }
            else
            {
                H3 = h2;
                h2 = h2->sibling;
            }

            if(H3_prev == NULL)
            {
                H3_prev = H3;
                heap = H3;
            }
            else
            {
                H3_prev->sibling = H3;
                H3_prev = H3;
            }
        }//while

        if(h1 == NULL)
            H3_prev->sibling = h2;
        else if(h2 == NULL)
            H3_prev->sibling = h1;
    }

    if(H1 == NULL)
    {
        heap = H2;
    }
    if(H2 == NULL)
    {
        heap = H1;
    }

    return heap;
}

//连接2个度数k相同的二项树成度数为k+1的一棵树,将H1 连接到 H2上
BinHeap BinHeapPair(BinHeap H1, BinHeap H2)
{
    H1->parent = H2;
    H1->sibling = H2->leftChild;
    H2->leftChild = H1;
    H2->degree++;
    return H2;
}

2.5 插入一个结点

先创建只有该结点的二项堆,然后在与原来的二项堆合并。

//用数组元素创建二项队列
BinHeap CreatHeapWithArray(Item a[], int n)
{
    BinHeap heap = NULL;
    BinHeap newheap = NULL;
    for(int i = 0; i < n; i++)
    {
        newheap = (BinHeap)malloc(sizeof(*newheap));
        if(newheap == NULL)
        {
            printf("Memory is not sufficient\n");
            exit(1);
        }
        memset(newheap, 0, sizeof(*newheap));
        newheap->key = a[i];
        if(heap == NULL)
            heap = newheap;
        else
        {
            heap = BinHeapUnion(heap, newheap);
            newheap = NULL;
        }

    }
    return heap;
}

2.6 删除最小

从根表中找到最小关键字的结点,将以该结点为根的整棵二项树从堆取出,删除取出的二项树的根,将其剩下的子女倒序排列,组成了一个新的二项堆,再与之前的二项堆合并。

BinHeap BinHeapDelMin(BinHeap H1)
{
    if (H1 == NULL)
        return NULL;

    BinHeap p, min, min_prev;
    p = H1;
    min_prev = NULL;
    min = H1;
    Item key = min->key;

    while (p->sibling != NULL)
    {
        if(p->sibling->key < key)
        {
            min_prev = p;
            min = p->sibling;
            key = p->sibling->key;
        }
        p = p->sibling;
    }

    BinHeap heap = H1;

    if(H1 == min)
        heap = min->sibling;
    else
        min_prev->sibling = min->sibling;

    BinHeap x_prev = NULL, x = NULL; //
    BinHeap  H2 = NULL;
    p = NULL;
    x = min->leftChild;
    while (x != NULL)
    {
        p = x;
        x = x->sibling;
        p->sibling = H2;
        H2 = p;
        p->parent = NULL;
    }
    heap = BinHeapUnion(heap, H2);
    return min;
}

3 工程源码

http://pan.baidu.com/s/1o6LuRCQ

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值