数据结构与算法分析:斐波那契堆

该博客介绍了C++实现斐波那契堆的详细过程,包括插入、合并、删除最小节点、更新节点键值等功能。斐波那契堆是一种高效的可并堆,具有优秀的摊还时间复杂度。文章提供了具体的代码实现,并通过一系列测试用例验证了其正确性。
摘要由CSDN通过智能技术生成

参考 斐波那契堆(二)之 C++的实现斐波那契堆的C++实现,可参考视频 【B站首发】来学斐波那契堆吧♪(^∀^●)
势函数的作用:得到进行一次操作的代价和势函数的变化关系: c o s t i = Δ ϕ + k cost_i=\Delta \phi +k costi=Δϕ+k,累加求 N N N c o s t i cost_i costi
合并两个双向循环链表 catList 操作如下图:
请添加图片描述

#include <iomanip>
#include <iostream>
#include <cstdlib>
#include <cmath>
using namespace std;

template <class T>
class FibNode {
    public:
        T data;                // 存储的值
        int degree;            // 度数,即此节点的子节点数目
        FibNode<T> *left;       // 左兄弟
        FibNode<T> *right;      // 右兄弟
        FibNode<T> *child;      // 第一个孩子节点
        FibNode<T> *parent;    // 父节点
        bool marked;            //指示节点自从上一次成为另一个节点的孩子之后,是否失去过孩子

        FibNode(T d):data(d),marked(false),degree(0),child(nullptr),parent(nullptr),left(this),right(this){};
};

template <class T>
class FibHeap {
    //斐波那契堆,高效可并堆,摊还时间界最优
    //主要包括一列堆序树集合,有多个子节点
    //第一层树的根串成了环形双向链表,称为斐波那契堆的根链表
    //对每棵树而言,每层子节点也串成双向循环链表,通过child指针和下一层双向循环链表沟通
    //斐波那契堆的一些操作要尽可能长地延后执行,不同的操作可以进行性能平衡
    private:
        int currentSize;         // 堆中节点的总数
        int maxDegree;          // 斐波那契堆中节点的最大孩子数目
        FibNode<T> *minRoot;    // 最小节点(某个最小堆的根节点)
        FibNode<T> **cons;    // 最大度的内存区域

    public:
        FibHeap():currentSize(0),maxDegree(0),minRoot(nullptr),cons(nullptr){};

        // 新建data对应的节点,并将其插入到斐波那契堆中
        void insert(T data){
            FibNode<T>* node = new FibNode<T>(data);
            if (!node) return ;
            insert(node);
        }
        // 将rhs合并到当前堆中
        void merge(FibHeap<T> *rhs){
            if (!rhs) return ;
            if(rhs->maxDegree > maxDegree) swap(*this, *rhs);
            if(!minRoot){                // this无"最小节点"
                minRoot = rhs->minRoot;
                currentSize = rhs->currentSize;
            }                                   // this有"最小节点" && rhs有"最小节点"
            else if(!rhs->minRoot){
                // 将"rhs中根链表"添加到"this"中
                catList(minRoot, rhs->minRoot);
                if (minRoot->data > rhs->minRoot->data)
                    minRoot = rhs->minRoot;
                currentSize += rhs->currentSize;
            }
            free(rhs->cons);
            delete rhs;
        }
        // 移除斐波那契堆中的最小节点
        void removeMin(){
            if (!minRoot) return;
            FibNode<T> *child = nullptr;
            FibNode<T> *m = minRoot;
            // 将minRoot每一个儿子(儿子和儿子的兄弟)都添加到"斐波那契堆的根链表"中
            while (m->child){
                child = m->child;
                removeNode(child);
                if (child->right == child) m->child=nullptr;
                else m->child = child->right;
                insertToRootList(child, minRoot);
                child->parent = nullptr;
            }
            // 将m从根链表中移除
            removeNode(m);
            // 若m是堆中唯一节点,则设置堆的最小节点为nullptr;
            // 否则,设置堆的最小节点为一个非空节点(m->right),然后再进行调节。
            if (m->right == m) minRoot=nullptr;
            else{
                minRoot = m->right;
                consolidate();
            }
            currentSize--;
            delete m;
        }

        // 获取斐波那契堆中最小键值,并保存到pdata中;成功返回true,否则返回false。
        bool minRootimum(T *pdata);
        // 将斐波那契堆中键值olddata更新为newdata
        void update(T olddata, T newdata);
        // 删除键值为data的节点
        void remove(T data);
        // 斐波那契堆中是否包含键值data
        bool contains(T data);
        // 打印斐波那契堆
        void print();
        // 销毁
        void destroy();

    private:
        // 将节点node插入到斐波那契堆中
        void insert(FibNode<T> *node){
            if (currentSize == 0) minRoot = node;
            else{
                insertToRootList(node, minRoot);
                if (node->data < minRoot->data) minRoot = node;
            }
            currentSize++;
        }
        // 将node从双向循环链表移除
        void removeNode(FibNode<T> *node){
            node->left->right = node->right;
            node->right->left = node->left;
        }
        // 将node堆结点加入root结点之前(循环链表中)
        void insertToRootList(FibNode<T> *node, FibNode<T> *root){
            node->left        = root->left;
            root->left->right = node;
            node->right       = root;
            root->left        = node;
        }
        // 将双向链表b链接到双向链表a的后面
        void catList(FibNode<T> *a, FibNode<T> *b){
            FibNode<T>* tmp= a->right;
            a->right       = b->right;
            b->right->left = a;
            b->right       = tmp;
            tmp->left      = b;
        }

        // 将"堆的最小结点"从根链表中移除,
        FibNode<T>* extractMin(){
            FibNode<T> *p = minRoot;
            if (p == p->right) minRoot = nullptr;
            else{
                removeNode(p);
                minRoot = p->right;
            }
            p->left = p->right = p;
            return p;
        }
        // 将node链接到root根结点,node称为root的孩子节点
        void link(FibNode<T>* node, FibNode<T>* root){
            // 将node从双链表中移除
            removeNode(node);
            // 将node设为root的孩子
            if (!root->child) root->child = node;
            else insertToRootList(node, root->child);
            node->parent = root;
            root->degree++;
            node->marked = false;
        }
        // 创建consolidate所需空间
        void makeCons(){
            int old = maxDegree;
            // 计算log2(currentSize),"+1"意味着向上取整!
            // ex. log2(13) = 3,向上取整为3+1=4。
            maxDegree = (log(currentSize)/log(2.0)) + 1;
            if (old >= maxDegree) return ;
            // 因为度为maxDegree可能被合并,所以要maxDegree+1
            cons = (FibNode<T> **)realloc(cons,sizeof(FibHeap<T> *) * (maxDegree + 1));
        }
        // 合并斐波那契堆的根链表中左右相同度数的树
        void consolidate(){
            int i, d, D;
            FibNode<T> *x, *y, *tmp;
            makeCons();//开辟哈希所用空间
            D = maxDegree + 1;
            for (i = 0; i < D; i++) cons[i] = nullptr;
            // 合并相同度的根节点,使每个度数的树唯一
            while (minRoot){
                x = extractMin();                // 取出堆中的最小树(最小节点所在的树)
                d = x->degree;                    // 获取最小树的度数
                // cons[d] != nullptr,意味着有两棵树(x和y)的"度数"相同。
                while (cons[d]){
                    y = cons[d];                // y是"与x的度数相同的树"
                    if (x->data > y->data) swap(x, y);       // 保证x的键值比y小
                    link(y, x);    // 将y链接到x中
                    cons[d] = nullptr;
                    d++;
                }
                cons[d] = x;
            }
            minRoot = nullptr;
            // 将cons中的结点重新加到根表中
            for (i=0; i<D; i++){
                if (cons[i]){
                    if (!minRoot) minRoot = cons[i];
                    else{
                        insertToRootList(cons[i], minRoot);
                        if ((cons[i])->data < minRoot->data) minRoot = cons[i];
                    }
                }
            }
        }
        // 修改度数
        void renewDegree(FibNode<T> *parent, int degree);
        // 将node从父节点parent的子链接中剥离出来,并使node成为"堆的根链表"中的一员。
        void cut(FibNode<T> *node, FibNode<T> *parent);
        // 对节点node进行"级联剪切"
        void cascadingCut(FibNode<T> *node) ;
        // 将斐波那契堆中节点node的值减少为data
        void decrease(FibNode<T> *node, T data);
        // 将斐波那契堆中节点node的值增加为data
        void increase(FibNode<T> *node, T data);
        // 更新斐波那契堆的节点node的键值为data
        void update(FibNode<T> *node, T data);
        // 在最小堆root中查找键值为data的节点
        FibNode<T>* search(FibNode<T> *root, T data);
        // 在斐波那契堆中查找键值为data的节点
        FibNode<T>* search(T data);
        // 删除结点node
        void remove(FibNode<T> *node);
        // 销毁斐波那契堆
        void destroyNode(FibNode<T> *node);
        // 打印"斐波那契堆"
        void print(FibNode<T> *node, FibNode<T> *prev, int direction);
};



/*
 * 获取斐波那契堆中最小键值,并保存到pdata中;成功返回true,否则返回false。
 */
template <class T>
bool FibHeap<T>::minRootimum(T *pdata)
{
    if (minRoot==nullptr || pdata==nullptr)
        return false;

    *pdata = minRoot->data;
    return true;
}

/*
 * 修改度数
 */
template <class T>
void FibHeap<T>::renewDegree(FibNode<T> *parent, int degree)
{
    parent->degree -= degree;
    if (parent-> parent != nullptr)
        renewDegree(parent->parent, degree);
}

/*
 * 将node从父节点parent的子链接中剥离出来,
 * 并使node成为"堆的根链表"中的一员。
 */
template <class T>
void FibHeap<T>::cut(FibNode<T> *node, FibNode<T> *parent)
{
    removeNode(node);
    renewDegree(parent, node->degree);
    // node没有兄弟
    if (node == node->right)
        parent->child = nullptr;
    else
        parent->child = node->right;

    node->parent = nullptr;
    node->left = node->right = node;
    node->marked = false;
    // 将"node所在树"添加到"根链表"中
    insertToRootList(node, minRoot);
}

/*
 * 对节点node进行"级联剪切"
 *
 * 级联剪切:如果减小后的结点破坏了最小堆性质,
 *     则把它切下来(即从所在双向链表中删除,并将
 *     其插入到由最小树根节点形成的双向链表中),
 *     然后再从"被切节点的父节点"到所在树根节点递归执行级联剪枝
 */
template <class T>
void FibHeap<T>::cascadingCut(FibNode<T> *node)
{
    FibNode<T> *parent = node->parent;
    if (parent != nullptr)
    {
        if (node->marked == false)
            node->marked = true;
        else
        {
            cut(node, parent);
            cascadingCut(parent);
        }
    }
}

/*
 * 将斐波那契堆中节点node的值减少为data
 */
template <class T>
void FibHeap<T>::decrease(FibNode<T> *node, T data)
{
    FibNode<T> *parent;

    if (minRoot==nullptr ||node==nullptr)
        return ;

    if ( data>=node->data)
    {
        cout << "decrease failed: the new data(" << data <<") "
             << "is no smaller than current data(" << node->data <<")" << endl;
        return ;
    }

    node->data = data;
    parent = node->parent;
    if (parent!=nullptr && node->data < parent->data)
    {
        // 将node从父节点parent中剥离出来,并将node添加到根链表中
        cut(node, parent);
        cascadingCut(parent);
    }

    // 更新最小节点
    if (node->data < minRoot->data)
        minRoot = node;
}

/*
 * 将斐波那契堆中节点node的值增加为data
 */
template <class T>
void FibHeap<T>::increase(FibNode<T> *node, T data)
{
    FibNode<T> *child, *parent, *right;

    if (minRoot==nullptr ||node==nullptr)
        return ;

    if (data <= node->data)
    {
        cout << "increase failed: the new data(" << data <<") "
             << "is no greater than current data(" << node->data <<")" << endl;
        return ;
    }

    // 将node每一个儿子(不包括孙子,重孙,...)都添加到"斐波那契堆的根链表"中
    while (node->child != nullptr)
    {
        child = node->child;
        removeNode(child);               // 将child从node的子链表中删除
        if (child->right == child)
            node->child = nullptr;
        else
            node->child = child->right;

        insertToRootList(child, minRoot);       // 将child添加到根链表中
        child->parent = nullptr;
    }
    node->degree = 0;
    node->data = data;

    // 如果node不在根链表中,
    //     则将node从父节点parent的子链接中剥离出来,
    //     并使node成为"堆的根链表"中的一员,
    //     然后进行"级联剪切"
    // 否则,则判断是否需要更新堆的最小节点
    parent = node->parent;
    if(parent != nullptr)
    {
        cut(node, parent);
        cascadingCut(parent);
    }
    else if(minRoot == node)
    {
        right = node->right;
        while(right != node)
        {
            if(node->data > right->data)
                minRoot = right;
            right = right->right;
        }
    }
}

/*
 * 更新斐波那契堆的节点node的键值为data
 */
template <class T>
void FibHeap<T>::update(FibNode<T> *node, T data)
{
    if(data < node->data)
        decrease(node, data);
    else if(data > node->data)
        increase(node, data);
    else
        cout << "No need to update!!!" << endl;
}

template <class T>
void FibHeap<T>::update(T olddata, T newdata)
{
    FibNode<T> *node;

    node = search(olddata);
    if (node!=nullptr)
        update(node, newdata);
}

/*
 * 在最小堆root中查找键值为data的节点
 */
template <class T>
FibNode<T>* FibHeap<T>::search(FibNode<T> *root, T data)
{
    FibNode<T> *t = root;    // 临时节点
    FibNode<T> *p = nullptr;    // 要查找的节点

    if (root==nullptr)
        return root;

    do
    {
        if (t->data == data)
        {
            p = t;
            break;
        }
        else
        {
            if ((p = search(t->child, data)) != nullptr)
                break;
        }
        t = t->right;
    } while (t != root);

    return p;
}

/*
 * 在斐波那契堆中查找键值为data的节点
 */
template <class T>
FibNode<T>* FibHeap<T>::search(T data)
{
    if (minRoot==nullptr)
        return nullptr;

    return search(minRoot, data);
}

/*
 * 在斐波那契堆中是否存在键值为data的节点。
 * 存在返回true,否则返回false。
 */
template <class T>
bool FibHeap<T>::contains(T data)
{
    return search(data)!=nullptr ? true: false;
}

/*
 * 删除结点node
 */
template <class T>
void FibHeap<T>::remove(FibNode<T> *node)
{
    T m = minRoot->data-1;
    decrease(node, m-1);
    removeMin();
}

template <class T>
void FibHeap<T>::remove(T data)
{
    FibNode<T> *node;

    if (minRoot==nullptr)
        return ;

    node = search(data);
    if (node==nullptr)
        return ;

    remove(node);
}

/*
 * 销毁斐波那契堆
 */
template <class T>
void FibHeap<T>::destroyNode(FibNode<T> *node)
{
    FibNode<T> *start = node;

    if(node == nullptr)
        return;

    do {
        destroyNode(node->child);
        // 销毁node,并将node指向下一个
        node = node->right;
        delete node->left;
    } while(node != start);
}

template <class T>
void FibHeap<T>::destroy()
{
    destroyNode(minRoot);
    free(cons);
}

/*
 * 打印"斐波那契堆"
 *
 * 参数说明:
 *     node       -- 当前节点
 *     prev       -- 当前节点的前一个节点(父节点or兄弟节点)
 *     direction  --  1,表示当前节点是一个左孩子;
 *                    2,表示当前节点是一个兄弟节点。
 */
template <class T>
void FibHeap<T>::print(FibNode<T> *node, FibNode<T> *prev, int direction)
{
    FibNode<T> *start=node;

    if (node==nullptr)
        return ;
    do
    {
        if (direction == 1)
            cout << setw(8) << node->data << "(" << node->degree << ") is "<< setw(2) << prev->data << "'s child" << endl;
        else
            cout << setw(8) << node->data << "(" << node->degree << ") is "<< setw(2) << prev->data << "'s next" << endl;

        if (node->child != nullptr)
            print(node->child, node, 1);

        // 兄弟节点
        prev = node;
        node = node->right;
        direction = 2;
    } while(node != start);
}

template <class T>
void FibHeap<T>::print()
{
    int i=0;
    FibNode<T> *p;

    if (minRoot==nullptr)
        return ;

    cout << "== 斐波那契堆的详细信息: ==" << endl;
    p = minRoot;
    do {
        i++;
        cout << setw(2) << i << ". " << setw(4) << p->data << "(" << p->degree << ") is root" << endl;

        print(p->child, p, 1);
        p = p->right;
    } while (p != minRoot);
    cout << endl;
}


#define DEBUG 0

// 共8个
int a[] = {12,  7, 25, 15, 28,
           33, 41,  1};
// 共14个
int b[] = {18, 35, 20, 42,  9,
           31, 23,  6, 48, 11,
           24, 52, 13,  2};

// 验证"基本信息(斐波那契堆的结构)"
void testBasic()
{
    int i;
    int blen=sizeof(b)/sizeof(b[0]);
    FibHeap<int>* hb=new FibHeap<int>();

    // 斐波那契堆hb
    cout << "== 斐波那契堆(hb)中依次添加: ";
    for(i=0; i<blen; i++)
    {
        cout << b[i] <<" ";
        hb->insert(b[i]);
    }
    cout << endl;
    cout << "== 斐波那契堆(hb)删除最小节点" << endl;
    hb->removeMin();
    hb->print();
}

// 验证"插入操作"
void testInsert()
{
    int i;
    int alen=sizeof(a)/sizeof(a[0]);
    FibHeap<int>* ha=new FibHeap<int>();

    cout << "== 斐波那契堆(ha)中依次添加: ";
    for(i=0; i<alen; i++)
    {
        cout << a[i] <<" ";
        ha->insert(a[i]);
    }
    cout << endl;
    cout << "== 斐波那契堆(ha)删除最小节点" << endl;
    ha->removeMin();
    ha->print();

    // 斐波那契堆hb
    cout << "== 插入50" << endl;
    ha->insert(50);
    ha->print();
}

// 验证"合并操作"
void testUnion()
{
    int i;
    int alen=sizeof(a)/sizeof(a[0]);
    int blen=sizeof(b)/sizeof(b[0]);
    FibHeap<int>* ha=new FibHeap<int>();
    FibHeap<int>* hb=new FibHeap<int>();

    cout << "== 斐波那契堆(ha)中依次添加: ";
    for(i=0; i<alen; i++)
    {
        cout << a[i] <<" ";
        ha->insert(a[i]);
    }
    cout << endl;
    cout << "== 斐波那契堆(ha)删除最小节点" << endl;
    ha->removeMin();
    ha->print();

    // 斐波那契堆hb
    cout << "== 斐波那契堆(hb)中依次添加: ";
    for(i=0; i<blen; i++)
    {
        cout << b[i] <<" ";
        hb->insert(b[i]);
    }
    cout << endl;
    cout << "== 斐波那契堆(hb)删除最小节点" << endl;
    hb->removeMin();
    hb->print();

    // 将"斐波那契堆hb"合并到"斐波那契堆ha"中。
    cout << "== 合并ha和hb" << endl;
    ha->merge(hb);
    ha->print();
}

// 验证"删除最小节点"
void testRemoveMin()
{
    int i;
    int alen=sizeof(a)/sizeof(a[0]);
    int blen=sizeof(b)/sizeof(b[0]);
    FibHeap<int>* ha=new FibHeap<int>();
    FibHeap<int>* hb=new FibHeap<int>();

    cout << "== 斐波那契堆(ha)中依次添加: ";
    for(i=0; i<alen; i++)
    {
        cout << a[i] <<" ";
        ha->insert(a[i]);
    }
    cout << endl;
    cout << "== 斐波那契堆(ha)删除最小节点" << endl;
    ha->removeMin();
    //ha->print();

    // 斐波那契堆hb
    cout << "== 斐波那契堆(hb)中依次添加: ";
    for(i=0; i<blen; i++)
    {
        cout << b[i] <<" ";
        hb->insert(b[i]);
    }
    cout << endl;
    cout << "== 斐波那契堆(hb)删除最小节点" << endl;
    hb->removeMin();
    //hb->print();

    // 将"斐波那契堆hb"合并到"斐波那契堆ha"中。
    cout << "== 合并ha和hb" << endl;
    ha->merge(hb);
    ha->print();


    cout << "== 删除最小节点" << endl;
    ha->removeMin();
    ha->print();
}

// 验证"减小节点"
void testDecrease()
{
    int i;
    int blen=sizeof(b)/sizeof(b[0]);
    FibHeap<int>* hb=new FibHeap<int>();

    // 斐波那契堆hb
    cout << "== 斐波那契堆(hb)中依次添加: ";
    for(i=0; i<blen; i++)
    {
        cout << b[i] <<" ";
        hb->insert(b[i]);
    }
    cout << endl;
    cout << "== 斐波那契堆(hb)删除最小节点" << endl;
    hb->removeMin();
    hb->print();

    cout << "== 将20减小为2" << endl;
    hb->update(20, 2);
    hb->print();
}

// 验证"增大节点"
void testIncrease()
{
    int i;
    int blen=sizeof(b)/sizeof(b[0]);
    FibHeap<int>* hb=new FibHeap<int>();

    // 斐波那契堆hb
    cout << "== 斐波那契堆(hb)中依次添加: ";
    for(i=0; i<blen; i++)
    {
        cout << b[i] <<" ";
        hb->insert(b[i]);
    }
    cout << endl;
    cout << "== 斐波那契堆(hb)删除最小节点" << endl;
    hb->removeMin();
    hb->print();

    cout << "== 将20增加为60" << endl;
    hb->update(20, 60);
    hb->print();
}

// 验证"删除节点"
void testDelete()
{
    int i;
    int blen=sizeof(b)/sizeof(b[0]);
    FibHeap<int>* hb=new FibHeap<int>();

    // 斐波那契堆hb
    cout << "== 斐波那契堆(hb)中依次添加: ";
    for(i=0; i<blen; i++)
    {
        cout << b[i] <<" ";
        hb->insert(b[i]);
    }
    cout << endl;
    cout << "== 斐波那契堆(hb)删除最小节点" << endl;
    hb->removeMin();
    hb->print();

    cout << "== 删除节点20" << endl;
    hb->remove(20);
    hb->print();
}

int main()
{
    // 验证"基本信息(斐波那契堆的结构)"
    testBasic();
    // 验证"插入操作"
    testInsert();
    // 验证"合并操作"
    testUnion();
    // 验证"删除最小节点"
    testRemoveMin();
    // 验证"减小节点"
    testDecrease();
    // 验证"增大节点"
    testIncrease();
    // 验证"删除节点"
    testDelete();

    return 0;
}


  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值