如果不对斐波那契堆做任何DECREASE-KEY或DELETE操作,则堆中每棵树就和二项树一样;但是如果执行这两种操作,在一些状态下必须要破坏二项树的特征,比如DECREASE-KEY或DELETE后,有的树高为k,但是结点个数却少于2^k。这种情况下,堆中的树不是二项树。
为什么要进行级联剪切,级联剪切要干什么?
如果仅仅要切除父结点y的一个结点x,则仅仅需要把结点x加入到根结点所在双向链表中,再检测y是否mark==true即可,这是因为斐波那契中的树并不一定是二项树,近似二项树也可以。当删除y的第二个结点时,对在该路径上所有mark域为true的非根结点,将其从所在的双向链表中删除,并将其加入到F堆的最小树的根节点组成的双向链表中,即只有在删除同一个结点偶数个孩子时,才要进行级联剪枝,来维护二项树性质,奇数个时(即一个),对树影响不大,莫管它,只标记一下即可。
为什么偶数个的时候要递归往上删除?
二项树中在深度为i处恰有Cki个结点(i=0, 1, 2, …, k)。试着如果不进行级联剪枝,就可以发现,稍微删得结点超过两三个,最后的树就会不成样子,毫无章法。但是如果进行了级联剪枝,在偶数个结点时进行级联剪切时,原来是C30=1, C31=3, C32=3, C33=1, 减少两个结点关键字后,变为:C20=1, C21=2, C22=1,二项式是对称的,所以,偶数个结点时进行级联剪枝可以保证类似上边的正好使二项式减少一个数量级。
http://mindlee.com/2011/09/29/fibonacci-heaps/
http://zh.wikipedia.org/wiki/%E4%BA%8C%E9%A1%B9%E5%A0%86
#include<iostream>
using namespace std;
//log(15)/log(1.618)=5.628
const int D=5;
struct node
{
int key,degree;
node *p,*child,*left,*right;
bool mark;
node(int x):key(x),degree(0),p(NULL),child(NULL),left(NULL),right(NULL),mark(false){}
};
struct FibHeap
{
int n;
node *min;
FibHeap():n(0),min(NULL){}
};
FibHeap makeFibHeap()
{
return FibHeap();
}
//insert、concatenate、remove都只处理左右关系(兄弟指针),
//不处理上下关系(父指针和孩子指针)。
//a和b都非空
void insert(node *a,node *b)
{
b->left=a->left;
b->right=a;
b->left->right=b;
b->right->left=b;
}
//将一个新结点x插入H的根链表
void fibHeapInsert(FibHeap &H,node *x)
{
if(!H.min)
{
x->left=x->right=x;
H.min=x;
}
else
{
insert(H.min,x);
if(x->key<H.min->key)H.min=x;
}
++H.n;
}
node *fibHeapMinimum(FibHeap H)
{
return H.min;
}
//a和b都非空
void concatenate(node *a,node *b)
{
node *tmp=a->left;
tmp->right=b->left;
a->left=b;
b->left->right=tmp;
b->left=a;
}
FibHeap fibHeapUnion(FibHeap &H1,FibHeap &H2)
{
if(!H1.min)return H2;
if(!H2.min)return H1;
concatenate(H1.min,H2.min);
if(H2.min->key<H1.min->key)H1.min=H2.min;
H1.n+=H2.n;
return H1;
}
//a非空
void remove(node *a)
{
a->left->right=a->right;
a->right->left=a->left;
}
void fibHeapLink(FibHeap &H,node *y,node *x)
{
remove(y);
//使y成为x的孩子
if(!x->child)
{
y->left=y->right=y;
x->child=y;
}
else insert(x->child,y);
y->p=x;
++x->degree;
y->mark=false;
}
void consolidate(FibHeap &H)
{
node *A[D+1]={NULL};
node *sentinel=H.min->left;
bool flag=true;
for(node *w=H.min,*next;flag;w=next)
{
if(w==sentinel)flag=false;
next=w->right;
node *x=w;
int d=x->degree;
while(A[d])
{
node *y=A[d];
if(x->key>y->key)
{
node *tmp=x;
x=y;
y=tmp;
}
fibHeapLink(H,y,x);
A[d++]=NULL;
}
A[d]=x;
}
H.min=NULL;
for(int i=0;i<=D;++i)
{
if(A[i])
{
if(!H.min)
{
A[i]->left=A[i]->right=A[i];
H.min=A[i];
}
else
{
insert(H.min,A[i]);
if(A[i]->key<H.min->key)H.min=A[i];
}
}
}
}
node *fibHeapExtractMin(FibHeap &H)
{
node *z=H.min;
if(z)
{
bool flag=true;
for(node *x=z->child,*next;x&&flag;x=next)
{
next=x->right;
if(next==z->child)flag=false;
insert(H.min,x);
x->p=NULL;
}
remove(z);
if(z==z->right)H.min=NULL;
else
{
H.min=z->right;
consolidate(H);
}
--H.n;
}
return z;
}
//切断
void cut(FibHeap &H,node *x,node *y)
{
if(x==x->right)y->child=NULL;
else
{
if(x==y->child)y->child=x->left;
remove(x);
}
--y->degree;
insert(H.min,x);
x->p=NULL;
x->mark=false;
}
//级联切断
void cascadingCut(FibHeap &H,node *y)
{
node *z=y->p;
if(z)
{
if(!y->mark)y->mark=true;
else
{
cut(H,y,z);
cascadingCut(H,z);
}
}
}
//减小关键字的代码流程基本就是:如果减小后的结点破坏了最小堆性质,则把它切下来(cut),
//即从所在双向链表中删除,并将其插到由最小树根节点形成的双向链表中,
//然后再从x->p到所在树根节点递归执行级联剪枝。
void fibHeapDecreaseKey(FibHeap &H,node *x,int k)
{
if(k>x->key)return;
x->key=k;
node *y=x->p;
if(y&&x->key<y->key)
{
cut(H,x,y);
cascadingCut(H,y);
}
if(x->key<H.min->key)H.min=x;
}
//删除一个节点
void fibHeapDelete(FibHeap &H,node *x)
{
fibHeapDecreaseKey(H,x,INT_MIN);
fibHeapExtractMin(H);
}
//执行fibHeapInsert之后
// H.min
// |
// 21-17-24-18-52-38-30-26-46-39-41-35-3-7-23
//
//执行fibHeapExtractMin之后
// H.min
// |
// 26-35----------7
// | | |
// 39-30 41 17----18-23
// | | |
// 46 21 38-24
// |
// 52
//
//执行第一次fibHeapDecreaseKey之后
// H.min
// |
// 26-35-16-------7
// | | | |
// 39-30 41 52 17-18-23
// | | |
// 46 21 24
//
//执行第二次fibHeapDecreaseKey之后
// H.min
// |
// 26-35-16-15-18-7
// | | | |
// 39-30 41 52 17-23
// | |
// 46 21
int main()
{
FibHeap H=makeFibHeap();
int a[]={23,7,21,3,17,24,18,52,38,30,26,46,39,41,35};
for(int i=0;i<15;++i)
{
node *x=new node(a[i]);
fibHeapInsert(H,x);
}
fibHeapExtractMin(H);
//x指向38,y指向24
node *x=H.min->child->left->child->left,*y=x->right;
fibHeapDecreaseKey(H,x,16);
fibHeapDecreaseKey(H,y,15);
return 0;
}