参考 斐波那契堆(二)之 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;
}