📜  斐波那契堆–插入和联合(1)

📅  最后修改于: 2023-12-03 15:26:13.770000             🧑  作者: Mango

斐波那契堆–插入和联合

简介

斐波那契堆是一种高效的数据结构,用于维护一些动态的数据集合,如图数据集合、网络最短路径等。它的插入和联合操作的时间复杂度都为O(1),而删除某个元素的时间复杂度为O(log n)。

斐波那契堆是由 Michael Fredman 和 Robert Tarjan 在1984年提出的,它的名称来源于斐波那契数列。这是因为斐波那契堆使用了斐波那契数列中的一些技巧来保持树的平衡。

插入操作

将一个元素插入到斐波那契堆中需要执行以下步骤:

  1. 创建一个含有单一元素的树,并将其插入到堆中。

  2. 更新堆的根节点指针,如果插入的元素比当前的最小元素还要小,则将最小元素指针指向新插入的元素。

  3. 更新堆中的度数表。树的度数是树中节点的子节点数目。将新插入的树的度数设置为0,然后将其插入到堆中。

  4. 合并堆中度数相同的树。如果插入元素的树的度数和已存在的树的度数相同,则将它们合并成一个更大的树,将度数增加1。合并树的过程需要比较根节点元素,将较小的元素设置为新树的根节点。

  5. 重复步骤4直到不存在度数相同的树。

联合操作

将两个斐波那契堆合并成一个新的堆需要执行以下步骤:

  1. 将其中的一个堆作为新堆,将另一个堆的根节点插入其中。

  2. 更新堆的根节点指针,如果插入的元素比当前的最小元素还要小,则将最小元素指针指向新插入的元素。

  3. 更新堆中的度数表。树的度数是树中节点的子节点数目。

  4. 合并堆中度数相同的树。如果存在度数相同的树,则将它们合并,将度数增加1。合并树的过程需要比较根节点元素,将较小的元素设置为新树的根节点。

  5. 重复步骤4直到不存在度数相同的树。

代码实现

下面是C++代码实现:

#include <queue>

using namespace std;

template <typename T>
class FibHeap {
    struct Node {
        T value;
        bool marked;
        Node *next, *prev, *parent, *child;

        Node(T v) : value(v), marked(false), next(this), prev(this), parent(nullptr), child(nullptr) {}
    };

    Node *min = nullptr;
    int n = 0;

    void insertNode(Node* node) {
        if(min == nullptr) {
            min = node;
        } else {
            node->next = min;
            node->prev = min->prev;
            min->prev->next = node;
            min->prev = node;
            if(node->value < min->value) {
                min = node;
            }
        }
        n++;
    }

    void removeNode(Node* node) {
        node->prev->next = node->next;
        node->next->prev = node->prev;
        n--;
    }

    void link(Node* x, Node* y) {
        removeNode(y);
        y->parent = x;
        y->marked = false;
        if(x->child == nullptr) {
            x->child = y;
            y->next = y;
            y->prev = y;
        } else {
            Node* c = x->child;
            y->next = c;
            y->prev = c->prev;
            c->prev->next = y;
            c->prev = y;
        }
        x->degree++;
    }

    void consolidate() {
        int D = log(n) / log(2) + 1;
        vector<Node*> A(D, nullptr);
        Node* w = min;
        do {
            Node* x = w;
            int d = x->degree;
            while(A[d] != nullptr) {
                Node* y = A[d];
                if(x->value > y->value) {
                    swap(x, y);
                }
                link(x, y);
                A[d] = nullptr;
                d++;
            }
            A[d] = x;
            w = w->next;
        } while(w != min);

        min = nullptr;
        for(int i = 0; i < D; i++) {
            if(A[i]) {
                insertNode(A[i]);
            }
        }
    }

    void cut(Node* x, Node* y) {
        removeNode(x);
        y->degree--;
        if(x->next == x) {
            y->child = nullptr;
        } else {
            y->child = x->next;
        }
        x->prev->next = x->next;
        x->next->prev = x->prev;
        insertNode(x);
        x->parent = nullptr;
        x->marked = false;
    }

    void cascadingCut(Node* y) {
        Node* z = y->parent;
        if(z) {
            if(y->marked == false) {
                y->marked = true;
            } else {
                cut(y, z);
                cascadingCut(z);
            }
        }
    }

public:
    bool empty() {
        return n == 0;
    }

    void insert(T value) {
        insertNode(new Node(value));
    }

    T pop() {
        Node* z = min;
        for(Node* x = z->child; x; x = x->next) {
            insertNode(x);
            x->parent = nullptr;
        }
        removeNode(z);
        if(z->next == z) {
            min = nullptr;
        } else {
            min = z->next;
            consolidate();
        }
        T res = z->value;
        delete z;
        return res;
    }

    void decreaseKey(Node* x, T value) {
        if(value > x->value) {
            throw exception("New value is greater than current value.");
        }
        x->value = value;
        Node* y = x->parent;
        if(y && x->value < y->value) {
            cut(x, y);
            cascadingCut(y);
        }
        if(x->value < min->value) {
            min = x;
        }
    }
};

int main() {
    FibHeap<int> heap;
    heap.insert(5);
    heap.insert(3);
    heap.insert(8);
    heap.decreaseKey(heap.min, 1);
    heap.pop(); // 1
    heap.pop(); // 3
    heap.pop(); // 8
    return 0;
}

以上代码实现了一个最小堆,可以插入元素、降低元素的值(decrease-key操作)和删除堆顶元素(pop操作)。堆元素支持拥有任何可比较的类型。这里使用了双向循环链表来保存堆中的节点。还实现了一个decrease-key操作,用于将某个元素的值降低到一个更小的值。