二项堆是可合并堆的数据结构,应该功能应该类似左偏树。
1)共有2k个结点。
2)树的高度为k。
3)在深度i处恰有Cik个结点。
4)根的度数(子女的个数)为k,它大于任何其他结点的度数;如果根的子女从左到右的编号设为k-1, k-2, …, 0,子女i是子树Bi的根。
struct BinHeapNode
{
int key,degree;//key是键值,degree表示子女个数
BinHeapNode *parent,*leftChild,*sibling;
//leftChild是节点x的最左孩子指针,sibling是指向x的紧右兄弟的指针
//也就是说,每层节点都有指针指向其右边的兄弟节点,如果某个节点是最右边的,那么sibling是null
};
//返回最小关键字的指针
BinHeapNode* BinHeapMin(BinHeapNode *heap)
{
BinHeapNode *y = NULL,*x = heap;
int min = 1<<30;
//由于符合最小堆的性质,所有最小值就在根节点那一行
while(x != NULL)
{
if(x->key < min)
{
y = x;
min = x->key;
}
x = x->sibling;//向右边走,到另一个二项树的根
}
return y;
}
//将以H1为根的树和以H2为根的树连接,使H2变成H1的父节点
void BinLink(BinHeapNode *&H1,BinHeapNode *&H2)
{
H1->parent = H2;
H1->sibling = H2->leftChild;//向右连接H2的最左孩子
H2->leftChild = H1;//更新H2的最左孩子
H2->degree++;//H2的degree++
}
BINOMIAL-HEAP-MERGE ,将H1和H2的根表合并成一个按度数的单调递增次序排列的链表
//将二项堆H1和H2的根表合并成一个按度数(degree)单调递增次序的链表,合并到H3然后给heap
//类似于归并排序过程
BinHeapNode* BinHeapMerge(BinHeapNode *&H1,BinHeapNode *&H2)
{
BinHeapNode *heap = NULL, *fHeap=NULL,*sHeap=NULL,*pre_H3=NULL,*H3=NULL;
fHeap = H1;
sHeap = H2;
while(fHeap != NULL && sHeap != NULL)
{
if(fHeap->degree <= sHeap->degree)
{
H3 = fHeap;
fHeap=fHeap->sibling;//向右走
}
else
{
H3 = sHeap;
sHeap = sHeap->sibling;//向右走
}
if(pre_H3 == NULL)//第一次
{
pre_H3 = H3;//把首节点给pre_H3
heap = H3;//给heap
}
else
{
pre_H3->sibling = H3;//pre_H3->sibling = H3
pre_H3 = H3;
}
}//while
if(fHeap != NULL) H3->sibling = fHeap;
else H3->sibling = sHeap;
return heap;
}
Binomil-Heap-Union伪代码:
BinHeapNode* BinHeapUnion(BinHeapNode *&H1,BinHeapNode *&H2)
{
BinHeapNode *heap = NULL,*pre_x = NULL,*x=NULL,*next_x=NULL;
heap = BinHeapMerge(H1,H2);
if(heap == NULL) return heap;
x = heap;
next_x = x->sibling;
while(next_x != NULL)
{
//case1: degree[x] != degree[sibling[x]]
if((x->degree!=next_x->degree)
//case2:degree[x]=degree[next_x]=degree[sibling[next_x]]
||(next_x->sibling!=NULL && x->degree == next_x->sibling->degree))
{//只需要向右移
pre_x = x;
x = next_x;
}
//case3:degree[x]=degree[next_x]!=degree[sibling[next_x]]&x->key <= next_x->key
//将sibling[x]连接到x上
else if(x->key <= next_x->key)
{
x->sibling = next_x->sibling;
BinLink(next_x,x);
}
else
{//将x连接到sibling[x]上
if(pre_x == NULL) heap = next_x;
else pre_x->sibling = next_x;
BinLink(x,next_x);
x = next_x;
}
next_x = x->sibling;
}
return heap;
}
BinHeapNode* BinHeapInsert(int key,BinHeapNode *heap)
{
BinHeapNode *newHeap = NULL;
newHeap = new BinHeapNode;
memset(newHeap,0,sizeof(BinHeapNode));//初始化新节点的值,degree,leftChild,sibling等
newHeap->key = key;
if(heap == NULL) heap = newHeap;
else
{
heap = BinHeapUnion(heap,newHeap);
newHeap = NULL;
delete newHeap;
}
return heap;
}
//抽取具有最小关键字的节点
BinHeapNode* BinHeapExtractMin(BinHeapNode *&heap)
{
BinHeapNode *pre_y=NULL,*y=NULL,*x=heap;
int min = 1<<30;
while(x != NULL)
{
if(x->key < min)
{
min = x->key;
pre_y = y;
y = x;
}
x = x->sibling;//向根表走
}//找到最小关键字的节点
if(y == NULL) return y;//空
//删除最小关键字节点
if(pre_y == NULL) heap = heap->sibling;
else pre_y->sibling = y->sibling;
//将x节点剩下的子女倒序排列,组成一个新的二项堆
BinHeapNode *H2 = NULL,*p=NULL;
x = y->leftChild;
//这里也就是将x节点的下一层改造成一个新的二项堆
while(x != NULL)
{
p = x;
x = x->sibling;
p->sibling = H2;
H2 = p;
p->parent = NULL;//由于x节点删除了就没有parent
}
//与之前的二项堆合并
heap = BinHeapUnion(heap,H2);
return y;
}
//减小关键字的值,将某一节点x的值key减小为一个新值key
void BinHeapDecreaseKey(BinHeapNode *heap,BinHeapNode *x,int key)
{
if(key > x->key) return;
x->key = key;
BinHeapNode *z = NULL,*y=NULL;
//需要维护最小堆的结构
y = x;
z = x->parent;
while(z != NULL && z->key > y->key)
{
swap(z->key,y->key);
y = z;
z = y->parent;//这里只需要向父节点比较
}
}
//找出一个关键字
BinHeapNode* BinHeapFind(BinHeapNode *&heap, int key)
{
BinHeapNode *p = NULL, *x = NULL;
p = heap;
while (p != NULL)
{
if (p->key == key)
{
return p;
}
else
{
if((x = BinHeapFind(p->leftChild, key)) != NULL)//一直向最左孩子走
{
return x;
}
p = p->sibling;
}
}
return NULL;
}
//删除一个关键字
//删除的原理非常简单,把关键字减小,让它到达根节点,然后剔除最小值即可
BinHeapNode* BinHeapDelete(BinHeapNode * &heap, int key)
{
BinHeapNode * x = NULL;
x = BinHeapFind(heap, key);//找到该关键字节点
if (x != NULL)
{
BinHeapDecreaseKey(heap, x, -(1<<30));
return BinHeapExtractMin(heap);
}
return x;
}