二项树与二项堆

二项树与二项堆

一、概念

1. 二项树

二项树是一种递归定义的有序树,其递归定义如下:
\((1)二项树B_0只有一个节点;\)
\((2)二项树B_k由两颗二项树B_{k-1}组成,其中一棵树是另一棵树的根的最左儿子。\)
如下图所示:

二项树具有如下性质:
\((1)B_k有2^k个节点;\)
\((2)B_k的高度为k;\)
\((3)B_k在深度i处恰好有C_k^i个节点,i\in [0,k];\)
\((4)根的度数为\)k\(,它大于任何其它节点的度数,并且如果根的儿子从左到右编号为k - 1,k - 2, ... , 0,儿 子i是子树B_i的根。\)

2. 二项堆

二项堆是指满足以下性质的二项树的集合:
$(1)每颗二项树都满足最小(大)堆性质; $
\((2)不能有两棵或以上的二项树具有相同的度数(包括度数为0)。\)
对于一个有\(n\)个节点的二项堆,它将有\([logn]+1\)棵树,且树的类型与\(n\)的二进制位一一对应,如\(n=13(1101)_2\),则该二项堆有\(B_3,B_2,B_0\)三棵树。如图所示:

二项堆各操作:

建立 合并 查找最小值 查找节点 插入节点 删除节点
O(1) O(logn) O(logn) O(logn) O(logn)(均摊O(1)) O(logn)

二、代码

合并:
现将两个二项堆中的二项树利用归并为度数单调不降的根链表,之后将其合并为新二项堆,过程类似模拟二进制加法。

点击查看代码
#ifndef BINOMIALHEAP_H
#define BINOMIALHEAP_H
template<class T>
class BinomialHeapNode
{
public:
    BinomialHeapNode<T> *parent, *child, *pre, *next;
    T value;
    int degree;
    BinomialHeapNode()
    {
        parent = child = pre = next = nullptr;
        value = T();
        degree = 0;
    }
    explicit BinomialHeapNode(T value)
    {
        parent = child = pre = next = nullptr;
        this->value = value;
        degree = 0;
    }
};

template<class T>
class BinomialHeap
{
private:
    BinomialHeapNode<T> *root;
    void release(BinomialHeapNode<T> *now)
    {
        if (now->child != nullptr)release(now->child);
        if (now->next != nullptr)release(now->next);
        delete now;
    }
    /*
     * 将两个二项堆的树的根表合并为度数单调递增的链表
     */
    static BinomialHeapNode<T> *mergeRoot(BinomialHeapNode<T> *root1, BinomialHeapNode<T> *root2)
    {
        BinomialHeapNode<T> *root = nullptr, *tail = nullptr;
        while (root1 != nullptr || root2 != nullptr)
        {
            BinomialHeapNode<T> *tmp = nullptr;
            if (root2 == nullptr || (root1 != nullptr && root1->degree <= root2->degree))
                tmp = root1, root1 = root1->next;
            else tmp = root2, root2 = root2->next;

            if (root == nullptr)root = tail = tmp;
            else tail->next = tmp, tmp->pre = tail, tail = tmp;
        }
        return root;
    }
    /*
     * 将两个度数相同的树合并
     */
    static void link(BinomialHeapNode<T> *child, BinomialHeapNode<T> *parent)
    {
        child->parent = parent;
        child->next = parent->child;
        if (parent->child != nullptr)parent->child->pre = child;
        parent->child = child;
        parent->degree++;
    }
    /*
     * 合并两个二项堆
    */
    static BinomialHeapNode<T> *combine(BinomialHeapNode<T> *root1, BinomialHeapNode<T> *root2)
    {
        BinomialHeapNode<T> *root = mergeRoot(root1, root2);
        //返回为空指针说明两个堆都是空的
        if (root == nullptr)
            return nullptr;
        BinomialHeapNode<T> *pre = nullptr, *now = root, *next = now->next;
        while (next != nullptr)
        {
            /*
             *根据当前相邻三棵树的种类,总共三种情况:
             *1.当前根与下一个根度数不同,说明树的类型不同,不做处理;
             *2.当前根、下一个根、再下一个根度数都相同,则跳过当前根,合并下两个;
             *3.当前根度数只与下一个根相等,根据两根值的大小进行合并。
            */
            if ((now->degree != next->degree) ||
                (next->next != nullptr && next->degree == next->next->degree))
            {
                pre = now;//pre 用来在之后连边
                now = next;
            }
            else if (now->value <= next->value)
            {
                now->next = next->next;
                next->pre = nullptr;//next在link中修改
                if (next->next != nullptr)next->next->pre = now;
                link(next, now);
            }
            else
            {
                if (pre == nullptr)root = next, next->pre = nullptr;
                else pre->next = next, now->pre = nullptr, next->pre = pre;
                link(now, next);
                now = next;
            }
            next = now->next;
        }
        return root;
    }
public:
    int size;
    BinomialHeap()
    {
        root = nullptr;
        size = 0;
    }
    ~BinomialHeap() { if (root != nullptr)release(root); }
    bool isEmpty() { return root == nullptr; }
    /*
     *  将heap合并到当前堆中
     */
    void merge(BinomialHeap<T> heap) { root = combine(root, heap.root); }
    void insert(T value)
    {
        auto *node = new BinomialHeapNode<T>(value);
        root = combine(root, node);
        size++;
    }
    /*
     * 获得最小值所在根节点
     */
    BinomialHeapNode<T> *getMinimumNode()
    {
        BinomialHeapNode<T> *now = root, *result = root;
        T minValue = result->value;
        while (now != nullptr)
        {
            if (now->value < minValue)result = now, minValue = now->value;
            now = now->next;
        }
        return result;
    }
    /*
     * 获得最小值
     */
    T getMinimum()
    {
        BinomialHeapNode<T> *node = getMinimumNode();
        return node->value;
    }
    /*
     * 删除最小值
     */
    void removeMinimum()
    {
        BinomialHeapNode<T> *node = getMinimumNode(), *heapNode = node->child;
        if (node == root)root = root->next;//如果是根节点需要换根
        if (node->pre != nullptr)node->pre->next = node->next, node->pre = nullptr;
        if (node->next != nullptr)node->next->pre = node->pre, node->next = nullptr;
        BinomialHeapNode<T> *now = heapNode;
        while (now != nullptr)now->parent = nullptr, now = now->next;
        delete node;
        size--;
        root = combine(root, heapNode);
    }
    /*
     * 没必要但姑且写一下删除某个特定值的方法:
     * 1.找到该点;
     * 2.将该点不断与父亲节点交换,直到其为根;
     * 3.断掉该点,将其子树合并回去即可。
     */
};

#endif //BINOMIALHEAP_H

posted @ 2022-06-15 19:57  nofind  阅读(373)  评论(0编辑  收藏  举报