定义:
二项队列不同于左式堆和二叉堆等优先队列的实现之处在于,一个二项队列不是一棵堆序的树,而是堆序树的集合,即森林。堆序树中的每棵树都是由约束形式的,叫做二项树。每一个高度上至多存在一棵二项树。高度为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
- typedef long ElementType;
-
- #define Infinity (30000L)
- #ifndef _BinHeap_H
- #define _BinHeap_H
-
- #define MaxTrees (14) //二项队列中的二项树高度最大为13;
- #define Capacity (16383) //高度0,1,2,3,...,13的二项树节点数目之和
-
- struct BinNode; //二项树节点
- typedef struct BinNode *BinTree; //二项树
- struct Collection;
- typedef struct Collection *BinQueue;
-
- BinQueue Initialize(void);
- void Destroy( BinQueue H);
- BinQueue MakeEmpty(BinQueue H);
- BinQueue Insert(ElementType Item, BinQueue H);
- ElementType DeleteMin(BinQueue H);
- BinTree CombineTrees( BinTree T1, BinTree T2 ); //合并两棵相同大小的树
- BinQueue Merge(BinQueue H1, BinQueue H2);
- ElementType FindMin(BinQueue H);
- int IsEmpty(BinQueue H);
- int IsFull(BinQueue H);
- #endif
|