優先隊列——二項隊列(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
node


【1】二項隊列相關

1.0)Attention: 二項隊列中不容許有高度相同的二項樹存在該隊列中; git

1.1)problem+solution:github

  • 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)二項樹的性質:spa

    • A1.1)從圖中看到, 二項樹Bk 由一個帶有兒子B0, B1, …, Bk-1的根組成;
    • A1.2)高度爲k 的二項樹剛好有2^k 個節點;
    • A1.3) 而在深度d 的節點數是 二項係數 。
  • A2)若是咱們把堆序添加到二項樹上, 並容許任意高度上最多有一顆二項樹,那麼咱們可以用二項樹的集合惟一地表示任意大小的優先隊列;code


【2】二項隊列操做(merge + insert + deleteMin)

2.1)合併操做(merge) (乾貨——合併操做的第一步就是查看是否有高度相同的二項樹,若是有的話將它們merge)blog

  • 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\n", root->value);            
        printPreorderChildSibling(depth + 1, root->leftChild);                                    
        printPreorderChildSibling(depth, root->nextSibling);
    } 
    else
    {
        for(i = 0; i < depth; i++)
            printf("    ");
        printf("NULL\n");
    }
}

// print Binominal Queue bq
void printBinominalQueue(BinominalQueue bq)
{
    int i;

    for(i=0; i<bq->capacity; i++)
    {
        printf("bq[%d] = \n", 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("\n=== after the  binominal queue bq is created ===\n");    
    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("\n=== after the binominal queue bq1 is created ===\n");    
    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("\n=== after the binominal queue bq2 is created ===\n");    
    printBinominalQueue(bq2);    
    // creating over.     

    // merge bq2 into the bq1 
    outerMerge(bq1, bq2);
     printf("\n=== after bq2 is merged into the bq1 ===\n");    
    printBinominalQueue(bq1);    
    // merge over. 
    
    // executing deleteMin opeartion towards binominal queue bq1    
    printf("\n=== after executing deleteMin opeartion towards binominal queue bq1 ===\n");    
    deleteMin(bq1);    
    printBinominalQueue(bq1);    
    // deleteMin over!
    return  0;
}

 

3.2) printing results

相關文章
相關標籤/搜索