二项堆的实现是怎样的

312 阅读4分钟

二项堆(Binomial Heap)是一种基于二项树(Binomial Tree)结构实现的堆(优先队列),是一种特殊的堆数据结构。它的特点是高效的插入、合并和删除最小元素操作。

二项树是一种递归定义的树结构,具有以下特点:

  1. 二项树的高度为h时,包含2^h个节点。
  2. 二项树的第i层有C(i,0)、C(i,1)、...、C(i,i)个节点,其中C(i,k)表示组合数。

二项堆由一组二项树构成,每个二项树都满足以下性质:

  1. 每个二项树都是严格递减的(最小堆)。
  2. 任意两个不同大小的二项树都不包含相同大小的根节点。

二项堆的主要操作包括插入、删除最小元素、合并等。下面是二项堆的基本操作:

  1. 插入(Insertion):将一个新元素插入到二项堆中,将其视为一个单节点的二项树,然后将其与原二项堆合并。
  2. 删除最小元素(Extract-Min):找到二项堆中最小的根节点,并将其从堆中删除,然后将其子树组成一个新的二项堆,并与原二项堆合并。
  3. 合并(Merge):将两个二项堆合并成一个新的二项堆,合并过程类似于二进制加法的进位操作。

二项堆的插入和合并操作的时间复杂度都是O(log n),其中n是二项堆中的节点数。删除最小元素操作的时间复杂度为O(log n),因为需要找到最小根节点,并对其子树进行合并。

实现二项堆时,通常使用指针来表示二项树节点,并采用一些技巧来提高操作的效率,如懒惰合并等。二项堆是一种非常重要的数据结构,在实际应用中被广泛使用。

实现一个二项堆?

以下是一个简单的C++实现二项堆的示例:

#include <iostream>
#include <vector>
#include <algorithm>

// 定义二项树节点
class BinomialTreeNode {
public:
    int key; // 节点值
    int degree; // 节点度数
    BinomialTreeNode* parent; // 父节点
    BinomialTreeNode* child; // 第一个子节点
    BinomialTreeNode* sibling; // 兄弟节点(右兄弟)

    // 构造函数
    BinomialTreeNode(int k) : key(k), degree(0), parent(nullptr), child(nullptr), sibling(nullptr) {}
};

// 合并两个二项树
BinomialTreeNode* mergeBinomialTrees(BinomialTreeNode* tree1, BinomialTreeNode* tree2) {
    if (!tree1) return tree2;
    if (!tree2) return tree1;
    if (tree1->key > tree2->key) std::swap(tree1, tree2); // 确保tree1的根节点值较小
    tree2->sibling = tree1->child; // 将tree2作为tree1的孩子
    tree1->child = tree2;
    tree1->degree++; // 更新tree1的度数
    return tree1;
}

// 合并两个二项堆
BinomialTreeNode* mergeBinomialHeaps(BinomialTreeNode* heap1, BinomialTreeNode* heap2) {
    BinomialTreeNode* newHeap = nullptr;
    BinomialTreeNode* carry = nullptr;
    while (heap1 || heap2 || carry) {
        // 将三个堆中的根节点进行合并
        BinomialTreeNode* root1 = heap1 ? heap1 : nullptr;
        BinomialTreeNode* root2 = heap2 ? heap2 : nullptr;
        BinomialTreeNode* root3 = carry ? carry : nullptr;
        BinomialTreeNode* mergedTree = nullptr;

        // 依次合并根节点
        if (root1 && root1->degree <= root2->degree && root1->degree <= root3->degree) {
            mergedTree = root1;
            heap1 = root1->sibling;
        } else if (root2 && root2->degree <= root1->degree && root2->degree <= root3->degree) {
            mergedTree = root2;
            heap2 = root2->sibling;
        } else {
            mergedTree = root3;
            carry = root3->sibling;
        }

        // 将合并后的树插入新堆中
        if (!newHeap) {
            newHeap = mergedTree;
        } else {
            newHeap = mergeBinomialTrees(newHeap, mergedTree);
        }
    }
    return newHeap;
}

// 插入元素到二项堆
BinomialTreeNode* insertIntoBinomialHeap(BinomialTreeNode* heap, int key) {
    BinomialTreeNode* newHeap = new BinomialTreeNode(key);
    return mergeBinomialHeaps(heap, newHeap);
}

// 删除二项堆中的最小元素
BinomialTreeNode* removeMinFromBinomialHeap(BinomialTreeNode* heap) {
    if (!heap) return nullptr;
    BinomialTreeNode* minNode = nullptr;
    BinomialTreeNode* prev = nullptr;
    BinomialTreeNode* curr = heap;
    BinomialTreeNode* minPrev = nullptr;

    // 找到最小元素及其前驱
    while (curr) {
        if (!minNode || curr->key < minNode->key) {
            minNode = curr;
            minPrev = prev;
        }
        prev = curr;
        curr = curr->sibling;
    }

    // 从堆中删除最小元素
    if (minPrev) {
        minPrev->sibling = minNode->sibling;
    } else {
        heap = minNode->sibling;
    }

    // 反转最小元素的子树,并将其与堆合并
    BinomialTreeNode* reversedChild = nullptr;
    curr = minNode->child;
    while (curr) {
        BinomialTreeNode* next = curr->sibling;
        curr->sibling = reversedChild;
        reversedChild = curr;
        curr = next;
    }
    heap = mergeBinomialHeaps(heap, reversedChild);

    delete minNode;
    return heap;
}

// 打印二项树节点
void printBinomialTree(BinomialTreeNode* root, int depth) {
    if (!root) return;
    for (int i = 0; i < depth; ++i) {
        std::cout << "  "; // 打印缩进
    }
    std::cout << root->key << std::endl; // 打印节点值
    printBinomialTree(root->child, depth + 1); // 递归打印子树
    printBinomialTree(root->sibling, depth); // 递归打印右兄弟节点
}

int main() {
    BinomialTreeNode* heap = nullptr;

    // 插入元素
    heap = insertIntoBinomialHeap(heap, 5);
    heap = insertIntoBinomialHeap(heap, 3);
    heap = insertIntoBinomialHeap(heap, 7);
    heap = insertIntoBinomialHeap(heap, 2);
    heap = insertIntoBinomialHeap(heap, 1);

    // 删除最小元素
    heap = removeMinFromBinomialHeap(heap);

    // 打印二项堆
    printBinomialTree(heap, 0);

    // 释放内存
    delete heap;

    return 0;
}