• 优先队列——二项队列(binominal queue)


    【0】README

    0.1) 本文文字描述部分转自 数据结构与算法分析, 旨在理解 优先队列——二项队列(binominal queue) 的基础知识; 
    0.2) 本文核心的剖析思路均为原创(insert,merge和deleteMin的操作步骤图片示例), 源代码均为原创; 
    0.3) for original source code, please visit https://github.com/pacosonTang/dataStructure-algorithmAnalysis/tree/master/chapter6/p152_binominal_queue


    【1】二项队列相关

    1.0)Attention: 二项队列中不允许有高度相同的二项树存在该队列中; 

    1.1)problem+solution:

    • 1.1.1)problem:虽然左式堆和斜堆每次操作花费O(logN)时间, 这有效地支持了合并, 插入和deleteMin, 但还是有改进的余地,因为我们知道, 二叉堆以每次操作花费常数平均时间支持插入。
    • 1.1.2)solution: 二项队列支持所有这三种操作(merge + insert + deleteMin), 每次操作的最坏情形运行时间为O(logN), 而插入操作平均花费常数时间; (干货——优先队列的三种基本操作——merge + insert + deleteMin)

    1.2)相关定义

    • 1.2.1) 二项队列定义: 二项队列不同于我们看到的所有优先队列的实现之处在于, 一个二项队列不是一颗堆序的树, 而是堆序树的集合,称为森林;(干货——二项队列的定义和构成,二项队列是二项树的集合,而二项树是一颗堆序树)
    • 1.2.2)二项树定义: 堆序树中的每一颗都是有约束的形式。 (干货——二项树的定义)
    • 1.2.3)二项树的构成:每一个高度上至多存在一颗二项树, 高度为0的二项树是一颗单节点树; 高度为k 的二项树Bk 通过将一颗二项树 Bk-1 附接到另一颗二项树Bk-1 的根上而构成;(干货——二项树的构成) 
      这里写图片描述

    对上图的分析(Analysis):

    • A1)二项树的性质:

      • A1.1)从图中看到, 二项树Bk 由一个带有儿子B0, B1, …, Bk-1的根组成;
      • A1.2)高度为k 的二项树恰好有2^k 个节点;
      • A1.3) 而在深度d 的节点数是 二项系数 。
    • A2)如果我们把堆序添加到二项树上, 并允许任意高度上最多有一颗二项树,那么我们能够用二项树的集合唯一地表示任意大小的优先队列;


    【2】二项队列操作(merge + insert + deleteMin)

    2.1)合并操作(merge) (干货——合并操作的第一步就是查看是否有高度相同的二项树,如果有的话将它们merge)

    • step1) H1 没有高度为0的二项树而H2有,所以将H2中高度为0的二项树直接作为H3的一部分;(直接的意思==中间不需要merge);
    • step2) H1 和 H2 中都有高度为1的二项树,将它们进行merge, 得到高度为2的二项树(根为12);
    • step3)现在存在三颗高度为2的二项树(根分别为12, 14, 23),将其中两个进行merge(如merge根为12 和 根为14 的二项树),得到高度为3的二项树;
    • step4)所以,最后,我们得到二项队列, 其集合包括:高度为0的二项树(根为13), 高度为1的二项树(根为23),高度为3的二项树(高度为12);

    Attention)

    • A1)显然,merge操作是按照高度升序依次进行的;
    • A2)最后得到的二项队列不存在高度相同的二项树,即使存在,也要将高度相同的二项树进行merge;
    • A3)二项队里中的二项树的高度不必囊括所有的升序实数,即不必一定是0, 1, 2, 3,4 等等; 也可以是0, 1, 3 等;
    • A4)单节点树的高度为0; (干货——树高度从零起跳) 
      这里写图片描述

    2.2)插入操作(insert) (干货——insert操作是merge操作的特例,而merge操作的第一步就是查看是否有高度相同的二项树,如果有的话将它们merge)

    • 2.2.1)插入操作实际上: 就是特殊情形的合并, 我们只需要创建一颗单节点树并执行一次merge;
    • 2.2.2)更准确地说: 如果元素将要插入的那个优先队列中不存在的最小的二项树是Bi, 那么运行时间与 i + 1 成正比; 
      这里写图片描述

    对上图的分析(Analysis):

    • A1) 4 插入之后,与B0(根为3)进行merge, 得到一颗高度为1的树B1’(根为3);
    • A2)将B1’ 与 B1(根为1) 进行merge 得到高度为2 的树B2’(根为1), 它是新的优先队列;
    • A3)在插入7之后的下一次插入又是一个坏情形, 因为需要三次merge操作;

    2.3)删除最小值操作(deleteMin)

    • step1)找出一颗具有最小根的二项树来完成, 令该树为Bk, 令原始序列为H;
    • step2)从H中除去Bk, 形成新的二项队列H’;
    • step3)再除去Bk的根, 得到一些二项树B0, B1, …, Bk-1, 它们共同形成优先队列H”;
    • step4) 合并H’ 和 H” , 操作结束; 

     

    【3】 source code and printing results

    3.1)source code at a glance 
    Attention)二项队列的实现源代码用到了 儿子兄弟表示法

    #include "binominal_queue.h" 
    
    #define MINIMAL 10000
    
    int minimal(BinominalQueue bq)
    {
        int capacity;
        int i;
        int minimal;
        int miniIndex;    
    
        minimal = MINIMAL;
        capacity = bq->capacity;
        for(i=0; i<capacity; i++)
        {
            if(bq->trees[i] && bq->trees[i]->value < minimal)
            {
                minimal = bq->trees[i]->value;
                miniIndex = i;
            }
        }
    
        return miniIndex;
    }
    
    // initialize the BinominalQueue with given capacity.
    BinominalQueue init(int capacity)
    {
        BinominalQueue queue;            
        BinominalTree* trees; 
        int i;
    
        queue = (BinominalQueue)malloc(sizeof(struct BinominalQueue));
        if(!queue)
        {
            Error("failed init, for out of space !");
            return queue;
        }    
        queue->capacity = capacity;
        
        trees = (BinominalTree*)malloc(capacity * sizeof(BinominalTree));
        if(!trees)
        {
            Error("failed init, for out of space !");
            return NULL;
        }    
        queue->trees = trees;
    
        for(i=0; i<capacity; i++)
        {
            queue->trees[i] = NULL;
        }
        
        return queue;
    }  
    
    // attention: the root must be the left child of the binominal tree.
    int getHeight(BinominalTree root)
    {
        int height;        
        if(root == NULL)
        {        
            return 0;        
        }
    
        height = 1;    
        while(root->nextSibling)
        {
            height++;
            root = root->nextSibling;
        }
    
        return height;
    }
    
    
    // merge BinominalQueue bq2 into bq1.
    void outerMerge(BinominalQueue bq1, BinominalQueue bq2)
    {
        int height;
        int i;
    
        for(i=0; i<bq2->capacity; i++)
        {
            height = -1;
            if(bq2->trees[i])
            {
                height = getHeight(bq2->trees[i]->leftChild);    
                // attention for the line above
                // height = height(bq2->trees[i]->leftChild); not height = height(bq2->trees[i]);
                merge(bq2->trees[i], height, bq1);
            }                    
        }        
    }
    
    // merge tree h1 and h2 = bq->trees[height], 
    // who represents the new tree and old one respectively.
    BinominalTree merge(BinominalTree h1, int height, BinominalQueue bq)
    {            
        if(h1 == NULL)
        {
            return h1;
        }
    
        if(bq->trees[height] == NULL) // if the queue don't has the B0 tree.
        {        
            bq->trees[height] = h1;
            return bq->trees[height];
        }
        else // otherwise, compare the new tree's height with that of old one.
        {         
            if(h1->value > bq->trees[height]->value) // the new should be treated as the parent of the old.
            {        
                innerMerge(bq->trees[height], height, h1, bq);
            }
            else // the old should be treated as the parent of the new.
            {
                innerMerge(h1, height, bq->trees[height], bq);
            }
        }  
    
        return h1;
    } 
    
    BinominalTree lastChild(BinominalTree root)
    {                
        while(root->nextSibling)
        {        
            root = root->nextSibling;
        }
    
        return root; 
    }
    
    // merge tree h1 and h2 = bq->trees[height], 
    // who represents the new tree and old one respectively.
    BinominalTree innerMerge(BinominalTree h1, int height, BinominalTree h2, BinominalQueue bq)
    {
        if(h1->leftChild == NULL)
        {
            h1->leftChild = h2;
        }
        else
        {
            lastChild(h1->leftChild)->nextSibling = h2;
            // attention for the line above
            // lastChild(h1->leftChild)->nextSibling = h2 not lastChild(h1)->nextSibling = h2
        }
        height++;
        bq->trees[height-1] = NULL;
        merge(h1, height, bq);    
        
        return h1;
    } 
    
    // insert an element with value into the priority queue.
    void insert(ElementType value, BinominalQueue bq)
    {
        TreeNode node;
    
        node = (TreeNode)malloc(sizeof(struct TreeNode));
        if(!node)
        {
            Error("failed inserting, for out of space !");
            return ;
        }
        node->leftChild= NULL;
        node->nextSibling = NULL;    
        node->value = value;    
            
        merge(node, 0, bq);            
    }
    
    // analog print node values in the binominal tree, which involves preorder traversal. 
    void printPreorderChildSibling(int depth, BinominalTree root)
    {            
        int i;
        
        if(root) {        
            for(i = 0; i < depth; i++)
                printf("    ");
            printf("%d
    ", root->value);            
            printPreorderChildSibling(depth + 1, root->leftChild);                                    
            printPreorderChildSibling(depth, root->nextSibling);
        } 
        else
        {
            for(i = 0; i < depth; i++)
                printf("    ");
            printf("NULL
    ");
        }
    }
    
    // print Binominal Queue bq
    void printBinominalQueue(BinominalQueue bq)
    {
        int i;
    
        for(i=0; i<bq->capacity; i++)
        {
            printf("bq[%d] = 
    ", i);
            printPreorderChildSibling(1, bq->trees[i]);
        }    
    }
    
    void deleteMin(BinominalQueue bq)
    {
        int i;    
        BinominalTree minitree;    
        BinominalTree sibling;
    
        i = minimal(bq);
        minitree = bq->trees[i]->leftChild; //minitree->value=51
        free(bq->trees[i]);
        bq->trees[i] = NULL;            
    
        while(minitree)
        {
            sibling = minitree->nextSibling;
            minitree->nextSibling = NULL;
            merge(minitree, getHeight(minitree->leftChild), bq);        
            minitree = sibling;
        }        
    }
    
    int main()
    {
        BinominalQueue bq, bq1, bq2;    
        int data[] =  {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11};
        int data1[] = {65, 24, 12, 51, 16, 18};    
        int data2[] = {24, 65, 51, 23, 14, 26, 13};
        int i;
        int capacity;    
    
        // creating the binominal queue bq starts.
        capacity = 7;
        bq = init(capacity);        
        for(i=0; i<capacity; i++)
        {
            insert(data[i], bq);
        }
        printf("
    === after the  binominal queue bq is created ===
    ");    
        printBinominalQueue(bq);    
        // creating over.
         
        // creating the binominal queue bq1 starts.
        capacity = 6;
        bq1 = init(capacity);        
        for(i=0; i<capacity; i++)
        {
            insert(data1[i], bq1);
        }
        printf("
    === after the binominal queue bq1 is created ===
    ");    
        printBinominalQueue(bq1);    
        // creating over.
    
        // creating the binominal queue bq2 starts.
        capacity = 7;
        bq2 = init(capacity);        
        for(i=0; i<capacity; i++)
        {
            insert(data2[i], bq2);
        }
        printf("
    === after the binominal queue bq2 is created ===
    ");    
        printBinominalQueue(bq2);    
        // creating over.     
    
        // merge bq2 into the bq1 
        outerMerge(bq1, bq2);
         printf("
    === after bq2 is merged into the bq1 ===
    ");    
        printBinominalQueue(bq1);    
        // merge over. 
        
        // executing deleteMin opeartion towards binominal queue bq1    
        printf("
    === after executing deleteMin opeartion towards binominal queue bq1 ===
    ");    
        deleteMin(bq1);    
        printBinominalQueue(bq1);    
        // deleteMin over!
        return  0;
    }

    3.2) printing results

  • 相关阅读:
    阿里消息队列中间件 RocketMQ 源码分析 —— Message 拉取与消费(上)
    数据库中间件 ShardingJDBC 源码分析 —— SQL 解析(三)之查询SQL
    数据库分库分表中间件 ShardingJDBC 源码分析 —— SQL 解析(六)之删除SQL
    数据库分库分表中间件 ShardingJDBC 源码分析 —— SQL 解析(五)之更新SQL
    消息队列中间件 RocketMQ 源码分析 —— Message 存储
    源码圈 300 胖友的书单整理
    数据库分库分表中间件 ShardingJDBC 源码分析 —— SQL 路由(一)分库分表配置
    数据库分库分表中间件 ShardingJDBC 源码分析 —— SQL 解析(四)之插入SQL
    数据库分库分表中间件 ShardingJDBC 源码分析 —— SQL 路由(二)之分库分表路由
    C#中Math类的用法
  • 原文地址:https://www.cnblogs.com/pacoson/p/5151886.html
Copyright © 2020-2023  润新知