二项队列的结构
二项队列不同于其他优先队列的实现之处在于,一个二项队列不是一颗堆序的树,而是堆序的树的集合,称为森林。堆序中的每颗树都有一定的约束形式,叫做二项树。每一个高度上至多存在一颗二项树。高度为0的二项树是一颗单节点树;高度为k的二项树Bk通过将一颗二项树Bk-1附接到另一颗二项树Bk-1的根上而构成。下图则是一个简单的二项队列。
二项队列的基本操作
二项队列的基本操作最主要的是合并,其次还有插入和删除最小值,但是插入我们也可以把它看作是合并的特殊形式之一,我们只需要创建一颗单节点树并执行一次合并即可。在编码实现中将会详细介绍到基本操作的方法。
编码实现
1).二项队列的实现
由于在删除最小值的操作中我们需要快速找出根的所有子树的能力,因此,需要一般树的标准表示方法——每个节点的儿子都存在一个链表中,而且每个节点都有指向它的第一个儿子(如果有的话)的指针。而且所有儿子还要按照它们子树的大小排序。总的来说,就是二项树的每个节点将包含数据、第一个儿子以及有兄弟。二项树中的诸儿子以递减次序排序
#define MaxTrees 30
#define Capacity 4294967296
typedef int ElementType;
typedef struct BinNode* Position;
typedef struct BinNode* BinTree;
typedef struct Collection* BinQueue;
struct BinNode {
ElementType Element;
Position LeftChild;
Position NextSibling;
};
struct Collection {
int CurrentSize;
BinTree TheTrees[MaxTrees];
};
2). 合并操作实现
合并操作的实现中主函数为Merge,该函数将H1和H2合并,把合并结果放入H1中,并清空H2。T1和T2分别是H1和H2中的二项树,而Carry则是从上一步中得来的树(可能为NULL).如果T1存在,则“!!T1”则是1, 反之则是0。由于有三个二项树,而每个二项树都有存在和不存在两种情况,则总共就有2^3 = 8种情况。因此我们的主函数Merge就需要使用switch来从这8种情况中解决。
BinTree CombineTrees(BinTree T1, BinTree T2)
{
if (T1->Element > T2->Element)
return CombineTrees(T2, T1);
T2->NextSibling = T1->LeftChild;
T1->LeftChild = T2;
return T1;
}
BinQueue Merge(BinQueue H1, BinQueue H2)
{
BinTree T1, T2, Carry = NULL;
int i, j;
if (H1->CurrentSize + H2->CurrentSize > Capacity)
{
printf("Merge would exceed capacity");
return;
}
H1->CurrentSize += H2->CurrentSize;
for (i = 0, j = 1; j <= H1->CurrentSize; i++, j *= 2)
{
T1 = H1->TheTrees[i];
T2 = H2->TheTrees[i];
switch (!!T1 + 2 * !!T2 + 4 * !!Carry)
{
case 0:/*没有树*/
case 1:/*只有H1*/
break;
case 2:/*只有H2*/
H1->TheTrees[i] = T2;
H2->TheTrees[i] = NULL;
break;
case 4:/*只有Carry*/
H1->TheTrees[i] = T2;
H2->TheTrees[i] = NULL;
break;
case 3:/*有H1和H2*/
Carry = CombineTrees(T1, T2);
H1->TheTrees[i] = NULL;
break;
case 5:/*H1和Carry*/
Carry = CombineTrees(T1, Carry);
H1->TheTrees[i] = NULL;
break;
case 6:/*H2和Carry*/
Carry = CombineTrees(T2, Carry);
H2->TheTrees[i] = NULL;
break;
case 7:/*全部都有*/
H1->TheTrees[i] = Carry;
Carry = CombineTrees(T1, T2);
H2->TheTrees[i] = NULL;
break;
}
}
return H1;
}
插入操作可以看做是合并操作中的特殊情况,这里就不详细写了。
3). 删除最小值
删除最小值的操作可以通过首先找出一颗具有最小根的二项树来完成。令该数为Bk, 并令原始的优先队列为H。我们从H的树的森林中除去二项树Bk, 形成新的二项树的队列H’。再除去Bk的根,得到一些二项树B0, B1,……, B-1, 它们共同形成优先队列H’。合并H’和H’’,操作结束。
int findMin(BinQueue H)
{
int i, min;
ElementType minvalue = 0x7FFFFFFF;
if(isEmpty(H))
return NULL;
for(i =0; i<MaxSize; i++)
{
if(H->theTrees[i])
if(H->theTrees[i]->Element <minvalue)
{
minvalue = H->theTrees[i]->Element;
min = i;
}
}
return min;
}
ElementType DeleteMin(BinQueue H)
{
int i;
if(isEmpty(H))
{
fprintf(stderr, "empty\n");
exit(1);
}
int min = findMin(H);
ElementType minValue;
BinTree DeletedTree, OldTree;
BinQueue DeletedQueue;
OldTree = H->theTrees[min];
minValue = OldTree ->Element;
DeletedTree = OldTree ->leftChild;
free (OldTree);
DeletedQueue = initialize();
DeletedQueue ->currentSize = (1<<min) -1;
for(i = min-1; i>=0; i--)
{
DeletedQueue ->theTrees[i] = DeletedTree;
DeletedTree = DeletedTree->nextSibling;
DeletedQueue ->theTrees[i]->nextSibling =NULL;
}
H->theTrees[min] = NULL;
H->currentSize -= DeletedQueue->currentSize+1;
merge(H, DeletedQueue);
return minValue;
}