二项队列分析及实现

来源:互联网 发布:dwg是什么软件 编辑:程序博客网 时间:2024/05/01 06:06

定义:

     二项队列不同于左式堆和二叉堆等优先队列的实现之处在于,一个二项队列不是一棵堆序的树,而是堆序树的集合,即森林。堆序树中的每棵树都是由约束形式的,叫做二项树。每一个高度上至多存在一棵二项树。高度为0的二项树是一颗单节点树,高度为k的二项树Bk通过将一棵二项树Bk-1附接到另一颗二项树Bk-1的根上而构成。下图显示二项树B0,B1,B2,B3以及B4。


二项队列的复杂度

        左堆的合并,插入,删除最小的时间复杂度为O(logN)。二项队列就是为了对这些结果进一步提高的一种数据结构。利用二项队列,这三种操作的最坏时间复杂度为O(logN),但是插入的平均时间复杂度为O(1)。

基本操作:
合并:
二项队列的合并操作可以看做一个简单的二进制加法运算,从低位运算到高位。首先是两个队列的B0相加,如果两个队列都存在B0(二进制为1),因此将两个B0合并成一个B1树,生成的B1树当做进位,参与下一步的B1运算,直到运算到最高位结束。


删除最小值/最大值:

删除最小值首先要做的事情就是找到最小值。那么只要寻找二项队列对应的每一刻Bk树的根节点中的最小值即可。然后把拥有最小值的Bk树删去根节点。此时剩下的树为B0,B1...Bk-1树。这些树构成一个新的二项队列,然后调用上述的合并操作,既可以完成删除操作。

插入:

插入操作等同于合并操作,非常好完成。

编码实现:

二项队列定义:

在对二项队列编码之前,需要明白如何表示二项队列。因为二项队列的根节点所指向的节点可能是无限的,所以不能像二叉树那样使用两个指针来指向两个儿子(这里有无数个儿子)。

具体的表示方式如下图所示:

第一张图代表我们画出来的二项队列。

第二张图上半部分的数组是指向树节点的指针,即指向Bk的根节点。

每个树节点有三个元素,Element,Leftchild, NextSibling。

其中NextSibling指的是和它本身同级的兄弟。如第一张图中的B3,

12没有同级兄弟,21,24,23互为同级兄弟,65,51,24互为同级兄弟。

那么Leftchild元素指向谁呢,当然是指向有最多孩子的节点,提取出来就是B2的23节点了。

理解了这里,在看第二张图想必会明白多了。

合并:

合并操作的主要内容就是做二进制加法运算,使用switch来进行判断。具体到两个Bk树的合并非常的简单,如下图所示:


然较小的根节点变成新的根,另一个Bk成为它的左孩子,它原来的左孩子成为另一个Bk根节点的兄弟。



详细代码:

头文件:

[cpp] view plain copy
  1. typedef long ElementType;  
  2.   
  3. #define Infinity    (30000L)  
  4. #ifndef  _BinHeap_H  
  5. #define _BinHeap_H  
  6.   
  7. #define MaxTrees    (14)        //二项队列中的二项树高度最大为13;  
  8. #define Capacity    (16383)     //高度0,1,2,3,...,13的二项树节点数目之和  
  9.       
  10. struct BinNode;                     //二项树节点  
  11. typedef struct BinNode *BinTree;    //二项树  
  12. struct Collection;  
  13. typedef struct Collection *BinQueue;  
  14.   
  15. BinQueue Initialize(void);  
  16. void Destroy( BinQueue H);  
  17. BinQueue MakeEmpty(BinQueue H);  
  18. BinQueue Insert(ElementType Item, BinQueue H);  
  19. ElementType DeleteMin(BinQueue H);  
  20. BinTree CombineTrees( BinTree T1, BinTree T2 );     //合并两棵相同大小的树  
  21. BinQueue Merge(BinQueue H1, BinQueue H2);  
  22. ElementType FindMin(BinQueue H);  
  23. int IsEmpty(BinQueue H);  
  24. int IsFull(BinQueue H);  
  25. #endif  
源文件:

[cpp] view plain copy
  1. #include "binomial.h"  
  2. #include <stdio.h>  
  3. #include <stdlib.h>  
  4.   
  5. typedef struct BinNode *Position;  
  6.   
  7. struct BinNode  
  8. {  
  9.     ElementType Element;  
  10.     Position LeftChild;  
  11.     Position NextSibling;  
  12. };  
  13.   
  14. struct Collection  
  15. {  
  16.     int CurrentSize;  
  17.     BinTree TheTrees[MaxTrees];     //数组,该数组每个元素都是一棵二项树  
  18. };  
  19.   
  20. BinQueue Initialize()  
  21. {  
  22.     BinQueue H;  
  23.     int i = 0;  
  24.     H = malloc(sizeof(struct Collection));  
  25.     if (H == NULL){  
  26.         printf("Out of space!!\n");  
  27.         return NULL;  
  28.     }  
  29.     H->CurrentSize = 0;      //设置二项队列初始大小为0,每棵二项树均为NULL  
  30.     for (i = 0; i < MaxTrees; ++i){  
  31.         H->TheTrees[i] = NULL;  
  32.     }  
  33.     return H;  
  34. }  
  35.   
  36. static void DestroyTree(BinTree T)  
  37. {  
  38.     //递归删除二项树的所有节点  
  39.     if (T != NULL){  
  40.         DestroyTree(T->LeftChild);  
  41.         DestroyTree(T->NextSibling);  
  42.         free(T);  
  43.     }  
  44. }  
  45.   
  46. void Destroy(BinQueue H)  
  47. {  
  48.     //释放二项队列所占空间:通过删除所有的二项树完成  
  49.     int i = 0;  
  50.     for (i = 0; i < MaxTrees; ++i){  
  51.         DestroyTree(H->TheTrees[i]);  
  52.     }  
  53. }  
  54.   
  55. BinQueue MakeEmpty(BinQueue H)  
  56. {  
  57.     int i = 0;  
  58.     Destroy(H);     //首先释放H所占空间  
  59.     for (i = 0; i < MaxTrees; ++i){  
  60.         H->TheTrees[i] = NULL;  
  61.     }  
  62.     H->CurrentSize = 0;      //二项队列当前大小  
  63.     return H;  
  64. }  
  65.   
  66. BinQueue Insert(ElementType Item, BinQueue H)  
  67. {  
  68.     BinTree NewNode;    //二项树B0  
  69.     BinQueue OneItem;   //只有B0的二项队列  
  70.     NewNode = malloc(sizeof(struct BinNode));  
  71.     if (NewNode == NULL){  
  72.         printf("Out of space!\n");  
  73.         return H;  
  74.     }  
  75.     NewNode->Element = Item;  
  76.     NewNode->LeftChild = NewNode->NextSibling = NULL;  
  77.     OneItem = Initialize();  
  78.     OneItem->CurrentSize = 1;  
  79.     OneItem->TheTrees[0] = NewNode;  
  80.     return Merge(H, OneItem);   //合并单节点的二项树构成的二项队列与H  
  81. }  
  82.   
  83. ElementType FindMin(BinQueue H)  
  84. {  
  85.     int i = 0;  
  86.     ElementType MinItem;  
  87.     if (IsEmpty(H)){  
  88.         printf("Empty binomial queue");  
  89.         return -1;  
  90.     }  
  91.     MinItem = Infinity;  
  92.     //遍历二项队列中的所有二项树,比较它们的根  
  93.     for (i = 0; i < MaxTrees; ++i){  
  94.         if (H->TheTrees[i] && H->TheTrees[i]->Element < MinItem){  
  95.             MinItem = H->TheTrees[i]->Element;  
  96.         }  
  97.     }  
  98.     return MinItem;  
  99. }  
  100.   
  101. int IsEmpty(BinQueue H)  
  102. {  
  103.     return H->CurrentSize == 0;      //currentsize存放二项队列中节点的个数  
  104. }  
  105.   
  106. int IsFull(BinQueue H)  
  107. {  
  108.     return H->CurrentSize == Capacity;  
  109. }  
  110.   
  111. BinTree CombineTrees( BinTree T1, BinTree T2 )  
  112. {  
  113.     //合并相同大小的两颗二项树  
  114.     if (T1 == NULL)  
  115.         return T2;  
  116.     else if (T2 == NULL)  
  117.         return T1;  
  118.     if (T1->Element > T2->Element)  
  119.         return CombineTrees(T2, T1);  
  120.     //根大的树做为根小的树的左儿子  
  121.     T2->NextSibling = T1->LeftChild;  
  122.     T1->LeftChild = T2;  
  123.     return T1;  
  124. }  
  125.   
  126. BinQueue Merge(BinQueue H1, BinQueue H2)  
  127. {  
  128.     BinTree T1, T2, Carry = NULL;  
  129.     int i = 0, j = 0;  
  130.     //首先判断合并是否会超出二项队列限制的大小  
  131.     if (H1->CurrentSize + H2->CurrentSize > Capacity){  
  132.         printf("Merge would exceed capacity!\n");  
  133.         return H1;  
  134.     }  
  135.     H1->CurrentSize += H2->CurrentSize;  
  136.     //遍历H1,H2中所有的二项树  
  137.     for (i = 0, j = 1; j <= H1->CurrentSize; ++i, j *= 2){  
  138.         T1 = H1->TheTrees[i];  
  139.         T2 = H2->TheTrees[i];  
  140.         //若T1为空,!!T1则为0,否则为1  
  141.         switch(!!T1 + 2* (!!T2) + 4 * (!!Carry)){  
  142.             case 0:  
  143.             case 1:  
  144.                 break;  
  145.             case 2:  
  146.                 //只有T2存在,直接将T2放入二项队列H1中对应的位置;  
  147.                 H1->TheTrees[i] = T2;  
  148.                 H2->TheTrees[i] = NULL;  
  149.                 break;  
  150.             case 3:  
  151.                 //T1与T2均存在,合并相同大小的二项树  
  152.                 Carry = CombineTrees(T1, T2);  
  153.                 H1->TheTrees[i] = NULL;  
  154.                 H2->TheTrees[i] = NULL;  
  155.                 break;  
  156.             case 4:  
  157.                 //由上一步合并而得的二项树作为二项队列H1的一部分  
  158.                 H1->TheTrees[i] = Carry;  
  159.                 Carry = NULL;  
  160.                 break;  
  161.             case 5:  
  162.                 Carry = CombineTrees(T1, Carry);  
  163.                 H1->TheTrees[i] = NULL;  
  164.                 break;  
  165.             case 6:  
  166.                 Carry = CombineTrees(T2, Carry);  
  167.                 H2->TheTrees[i] = NULL;  
  168.                 break;  
  169.             case 7:  
  170.                 H1->TheTrees[i] = Carry;  
  171.                 Carry = CombineTrees(T1, T2);  
  172.                 H2->TheTrees[i] = NULL;  
  173.                 break;  
  174.         }  
  175.     }  
  176.     return H1;  
  177. }  
  178.   
  179. ElementType DeleteMin(BinQueue H)  
  180. {  
  181.     int i = 0, j = 0;  
  182.     int MinTree;        //用来存放根最小的二项树的高度  
  183.     BinQueue DeletedQueue;  
  184.     Position DeletedTree, OldRoot;  
  185.     ElementType MinItem;  
  186.   
  187.     if (IsEmpty(H)){  
  188.         printf("Empty binomial queue!\n");  
  189.         return -1;  
  190.     }  
  191.   
  192.     MinItem = Infinity;  
  193.     //遍历二项队列,找出根元素最小的二项树  
  194.     for (i = 0; i < MaxTrees; ++i){  
  195.         if (H->TheTrees[i] && H->TheTrees[i]->Element < MinItem){  
  196.             MinItem = H->TheTrees[i]->Element;  
  197.             MinTree = i;  
  198.         }  
  199.     }  
  200.   
  201.     DeletedTree = H->TheTrees[MinTree];  
  202.     OldRoot = DeletedTree;  
  203.     DeletedTree = DeletedTree->LeftChild;  
  204.     free(OldRoot);      //删除根元素;  
  205.     DeletedQueue = Initialize();  
  206.   
  207.     //将1左移MinTree位,即得到高度为MinTree的二项树的大小  
  208.     //因为高度为k的二项树的大小是2^k,减1是因为删除了根  
  209.     DeletedQueue->CurrentSize = (1 << MinTree) -1;   
  210.       
  211.     //将删除根后的各个子树构成新的二项队列  
  212.     for (j = MinTree - 1; j >= 0; --j){                
  213.         DeletedQueue->TheTrees[j] = DeletedTree;  
  214.         DeletedTree = DeletedTree->NextSibling;  
  215.         DeletedQueue->TheTrees[j]->NextSibling = NULL;  
  216.     }  
  217.       
  218.     H->TheTrees[MinTree] = NULL;  
  219.     H->CurrentSize -= DeletedQueue->CurrentSize + 1;  //新二项队列的大小;  
  220.     Merge(H, DeletedQueue);   
  221.     return MinItem;  
  222. }  
0 0