二项堆(Binomial Heap)是一种基于二项树(Binomial Tree)结构实现的堆(优先队列),是一种特殊的堆数据结构。它的特点是高效的插入、合并和删除最小元素操作。
二项树是一种递归定义的树结构,具有以下特点:
- 二项树的高度为h时,包含2^h个节点。
- 二项树的第i层有C(i,0)、C(i,1)、...、C(i,i)个节点,其中C(i,k)表示组合数。
二项堆由一组二项树构成,每个二项树都满足以下性质:
- 每个二项树都是严格递减的(最小堆)。
- 任意两个不同大小的二项树都不包含相同大小的根节点。
二项堆的主要操作包括插入、删除最小元素、合并等。下面是二项堆的基本操作:
- 插入(Insertion):将一个新元素插入到二项堆中,将其视为一个单节点的二项树,然后将其与原二项堆合并。
- 删除最小元素(Extract-Min):找到二项堆中最小的根节点,并将其从堆中删除,然后将其子树组成一个新的二项堆,并与原二项堆合并。
- 合并(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;
}