• 堆之二项队列


    前面提到左式堆和斜堆,支持合并、插入、删除最小值(最小堆),且每次操作花费O(logn)。但是还有改进余地。

    二项队列支持上面的三相操作,每次操作最坏的情形运行时间为O(logn),而插入平均花费常数时间。

    二项队列结构

    二项队列不是一棵树,它是一个森林,由一组堆序的树组成的深林,叫做二项队列。二项堆是二项树的集合,所以先说明二项树。

    二项树的递归定义如下:

    • 二项树B0只有一个结点;
    • 二项树Bk由两棵二项树B(k-1)组成的,其中一棵树是另一棵树根的左孩子。

    二项树有以下性质:

    1. Bk共有2k个节点。
    2. Bk的高度为k。
    3. Bk在深度i处恰好有C(k,i)个节点,其中i=0,1,2,...,k。
    4. 根的度数为k,它大于任何其它节点的度数。

    注意:树的高度和深度是相同的。若只有一个节点的树的高度是0。

    二项堆是指满足以下性质的二项树的集合:

    • 每棵二项树都满足最小堆性质。即,父节点的关键字 <= 它的孩子的关键字。
    • 不能有两棵或以上的二项树具有相同的度数(包括度数为0)。换句话说,具有度数k的二项树有0个或1个。

    实际上,它将包含n个节点的二项堆,表示成若干个2的指数和(或者转换成二进制),则每一个2个指数都对应一棵二项树。例如,大小为13的优先队列就可以用B3,B2,B0来表示,二进制的表示为1101。

    下图是大小为13(0001 0101)的二项队列

    #define MaxTreeNum 32
    typedef int Type;
    
    typedef struct _BinomialNode{//二项树节点
        Type val;
        struct _BinomialNode *leftChild;//第一个孩子或者左孩子
        struct _BinomialNode *nextSibling;//右兄弟
    }BinomialNode, *BinomialTree;
    
    typedef struct _BinomialHeap{//二项队列
        int curSize;
        BinomialNode *trees[MaxTreeNum];//二项树数组
    }BinomialHeap;

    二项队列的操作

    查找最小项:只需要查找每个二项树的根节点就可以了,因此时间复杂度为O(logN)。

    合并:通过把两个队列相加在一起完成。因为有O(logN)棵树,所以合并的时间复杂度也是O(logN)。

    插入:插入也是一种合并,只不过是把插入的结点当做B0。虽然感觉插入的时间复杂度是O(logN),但是实际是O(1),因为有一定的概率是被插入的二项队列没有B0。

    删除最小:在根结点找到最小值,然后把最小值所在的树单独拿出分列为二项队列,然后把这个新的二项队列与原二项队列进行合并。每一个过程的时间复杂度为O(logN)。故加起来的时间复杂度仍为O(logN)。

    其中最重要的就是合并。

    合并的实现

    BinomialNode* mergeBinomialTree(BinomialTree t1, BinomialTree t2){
        if (t1->val > t2->val)return mergeBinomialTree(t2, t1);
        t2->nextSibling = t1->leftChild;
        t1->leftChild = t2;
        return t1;
    }
    BinomialHeap *mergeBinomialHeap(BinomialHeap *bh1, BinomialHeap *bh2){
        BinomialTree T1, T2, Carry = NULL;
        int i, j;
        if (bh1->curSize + bh2->curSize > MaxTreeNum)
            cerr << "Exceed the Capacity" << endl;
        bh1->curSize = bh1->curSize + bh2->curSize;
        for (i = 0, j = 1; j < bh1->curSize; i++, j *= 2){
            T1 = bh1->trees[i];
            T2 = bh2->trees[i];
            //!!T1 = T1 == NULL ? 0 : 1
            switch (!!T1 + 2 * !!T2 + 4 * !!Carry){
            case 0: //No Trees  
            case 1: //Only bh1  
                break;
            case 2:
                bh1->trees[i] = T2;
                bh2->trees[i] = NULL;
                break;
            case 4: //Only Carry  
                bh1->trees[i] = Carry;
                Carry = NULL;
                break;
            case 3: //T1,T2  
                Carry = mergeBinomialTree(T1, T2);
                bh1->trees[i] = bh2->trees[i] = NULL;
                break;
            case 5:
                Carry = mergeBinomialTree(T1, Carry);
                bh1->trees[i] = NULL;
                break;
            case 6:
                Carry = mergeBinomialTree(T2, Carry);
                bh2->trees[i] = NULL;
                break;
            case 7:
                bh1->trees[i] = Carry;
                Carry = mergeBinomialTree(T1, T2);
                bh2->trees[i] = NULL;
                break;
            }
        }
        return bh1;
    }

    同样插入删除是合并的特殊情况;删除的操作比较复杂,后面再补充。

    删除最小值的实现

  • 相关阅读:
    企业级应用框架设计备忘录
    DBHelper
    Oracle客户端精简绿色版 不安装oracle客户端 转载
    回车转TAB
    excel列显示列号 转载
    XtraTreeList Drag Event
    XmlSerializer vs DataContractSerializer: Serialization in Wcf 转载
    转载 在Windows64位环境下.net访问Oracle解决方案
    正则表达式学习笔记
    SQL相关子查询的例子
  • 原文地址:https://www.cnblogs.com/yeqluofwupheng/p/7455763.html
Copyright © 2020-2023  润新知