算法导论19(斐波那契堆)

本文深入探讨了斐波那契堆的工作原理,包括其在执行DECREASE-KEY和DELETE操作时如何通过级联剪切维持近似二项树特性。文章详细解释了为何需要进行级联剪切,并介绍了具体的实现代码。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

如果不对斐波那契堆做任何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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值