左式堆和斜堆已经很好的支持了插入、合并以及删除且都是级别。二项队列也支持这三种操作,且插入操作平均花费常数时间。
定义
- 二项队列不是堆,是堆的 collection ,每个堆都是二叉树
有
个儿子,
个节点,深度为
处有
个节点
-
结构
任意大小的堆都可以表示成二项树的 collection。
由于二项队列的结构较为特殊,有很多儿子,为了删除的快速和方便,我们用儿子-兄弟结构存放二项队列;为了合并的方便,从左到右子树按高度降序(descending)排列。操作
Merge & Insert
很像合成大西瓜……
插入是合并的特殊情况,不多赘述。可以看出合并时实际上也是在做二进制加法,这启发我们代码也可以这样写: ```cpp / merge equal-sized T1 and T2 / //实际做合并的驱动程序 BinTree CombineTrees( BinTree T1, BinTree T2 ){
/ attach the larger one to the smaller one / if ( T1->Element > T2->Element ){return CombineTrees( T2, T1 );
}
/ insert T2 to the front of the children list of T1 / T2->NextSibling = T1->LeftChild; T1->LeftChild = T2;
return T1; }
BinQueue Merge( BinQueue H1, BinQueue H2 ){
BinTree T1, T2, Carry = NULL;
int i, j; //j是计数器
if ( H1->CurrentSize + H2-> CurrentSize > Capacity ){
ErrorMessage();
}
H1->CurrentSize += H2-> CurrentSize;
for ( i=0, j=1; j<= H1->CurrentSize; i++, j*=2 ) {
T1 = H1->TheTrees[i]; T2 = H2->TheTrees[i]; /*current trees */
/* Carry T2 T1 */
//这里巧妙的一点是把指针用!!转化为0和1来计算
switch( 4*!!Carry + 2*!!T2 + !!T1 ) {
case 0: /* 000 */
case 1: /* 001 */ break; //这两种情况都不需要合并
case 2: /* 010 */
H1->TheTrees[i] = T2;
H2->TheTrees[i] = NULL;
break;
case 4: /* 100 */
H1->TheTrees[i] = Carry;
Carry = NULL;
break;
case 3: /* 011 */
Carry = CombineTrees( T1, T2 );
H1->TheTrees[i] = H2->TheTrees[i] = NULL;
break;
case 5: /* 101 */
Carry = CombineTrees( T1, Carry );
H1->TheTrees[i] = NULL;
break;
case 6: /* 110 */
Carry = CombineTrees( T2, Carry );
H2->TheTrees[i] = NULL;
break;
case 7: /* 111 */
H1->TheTrees[i] = Carry;
Carry = CombineTrees( T1, T2 );
H2->TheTrees[i] = NULL;
break;
} /* end switch */
} /* end for-loop */
return H1;
}
<a name="jVoUS"></a>
## DeleteMin
删除复杂一些,不过蕴含了合并操作<br />![image.png](https://cdn.nlark.com/yuque/0/2022/png/22181361/1647922979053-a887c4dc-751f-4775-be15-49409d0d839e.png#clientId=u10aeb3b2-4a08-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=364&id=uc9e363e4&margin=%5Bobject%20Object%5D&name=image.png&originHeight=618&originWidth=849&originalType=binary&ratio=1&rotation=0&showTitle=false&size=53066&status=done&style=shadow&taskId=u62c33883-322e-4c00-9090-f9e1d4d1eb6&title=&width=500)
```cpp
ElementType DeleteMin( BinQueue H ){
BinQueue DeletedQueue;
Position DeletedTree, OldRoot;
/* the minimum item to be returned */
ElementType MinItem = Infinity;
/* MinTree is the index of the tree with the minimum item */
int i, j, MinTree;
if ( IsEmpty( H ) ) {
PrintErrorMessage();
return –Infinity;
}
/* Step 1: find the minimum item */
for ( i = 0; i < MaxTrees; i++) {
if( H->TheTrees[i] && H->TheTrees[i]->Element < MinItem ) {
MinItem = H->TheTrees[i]->Element; MinTree = i;
}
}
DeletedTree = H->TheTrees[ MinTree ];
/* Step 2: remove the MinTree from H => H’ */
H->TheTrees[ MinTree ] = NULL;
/* Step 3.1: remove the root */
OldRoot = DeletedTree;
DeletedTree = DeletedTree->LeftChild;
free(OldRoot);
/* Step 3.2: create H” */
DeletedQueue = Initialize();
DeletedQueue->CurrentSize = ( 1<<MinTree ) – 1; /* 2^MinTree – 1 */
//这里的做法是将删除元素后产生的子树先整理成二项队列,以便后续合并
for ( j = MinTree – 1; j >= 0; j – – ) {
DeletedQueue->TheTrees[j] = DeletedTree;
DeletedTree = DeletedTree->NextSibling;
DeletedQueue->TheTrees[j]->NextSibling = NULL;
}
H->CurrentSize –= DeletedQueue->CurrentSize + 1;
/* Step 4: merge H’ and H” */
H = Merge( H, DeletedQueue );
return MinItem;
}