//fheap.h
//author:aether
//date:2012年12月17日
#ifndef FHEAP_H
#define FHEAP_H
#include
#include
//Remark:斐波那契堆不一定是二项树,而是最小堆有序树
namespace fib
{
#define nil 0
#define null -1
class FibHeap;
class Node
{
public:
friend class FibHeap;
Node(int k = 0):p(nil),c(nil),left(nil),right(nil),key(k),degree(0),mark(false){}
private:
Node *p;//指向父节点指针
Node *c;//指向任意子女的指针
Node *left;//指向左兄弟
Node *right;//指向右兄弟
int key;//关键字
size_t degree;//度数
bool mark;//标记值
};
class FibHeap
{
public:
FibHeap();
void insert(Node *x);
void heapUnion(FibHeap *fh);
Node* extractMin();
void decreaseKey(Node *x,int newKey);
void del(Node *x);
private:
void consolidDate();
void link(Node *x,Node *y);
void cut(Node *x,Node *y);
void cascadingCut(Node *y);
size_t num;//节点个数
Node *min;//最小节点
};
}
#endif
//fheap.cpp
//author:aether
//date:2012年12月17日
#include "fheap.h"
namespace fib
{
FibHeap::FibHeap()
{
num = 0;
min = nil;
}
void FibHeap::insert(Node *x)
{
x->left = x;
x->right = x;
if(min == nil)
min = x;
else
{
Node *left = min->left;
min->left = x;
left->right = x;
x->left = left;
x->right = min;
}
if(min != nil && x->key < min->key)
min = x;
num += 1;
}
void FibHeap::heapUnion(FibHeap *fh)
{
Node *x = fh->min;
if(x == nil || x == min)
return;
Node *a = x->left;
Node *b = x->right;
x->left = nil;
x->right = nil;
a->right = nil;
b->left = nil;
insert(x);
if(a != x)
{
while( a != nil)
{
Node *b = a->left;
insert(a);
a = b;
}
}
if(min == nil || (x->key < min->key))
min = x;
fh->min = nil;
fh->num = 0;
}
Node* FibHeap::extractMin()//真正进行合并的操作
{
Node *z = min;
if(z != nil)
{
//步骤①,剪短最小节点和子节点之间的关系,并且把子节点们放到根列表中
Node *child = z->c;
z->c = nil;
if(child != nil)
{
child->p = nil;
Node *left = child->left;
Node *right = child->right;
child->left = nil;
child->right = nil;
insert(child);
if(left != child)
{
while(left != nil)
{
Node *b = left->left;
left->p = nil;
insert(left);
left = b;
}
}
}
//步骤二,去除z节点
Node *left = z->left;
Node *right = z->right;
left->right = right;
right->left = left;
if(z == z->right)
min = nil;
else
{
min = z->right;
z->right = nil;
z->left = nil;
consolidDate();
}
num -= 1;
}
return z;
}
void FibHeap::decreaseKey(Node *x,int newKey)
{
if(x->key < newKey)
return;
x->key = newKey;
Node *y = x->p;
if(y != nil && y->key > x->key)
{
cut(x,y);
cascadingCut(y);
}
if(x->key < min->key)
min = x;
}
void FibHeap::del(Node *x)
{
decreaseKey(x,null);
extractMin();
}
void FibHeap::consolidDate()
{
size_t size = size_t(sqrt(float(num)))+1;
Node **A = static_cast(::operator new[](sizeof(Node*)*size));
for(size_t i = 0; i != size;++i)
A[i] = nil;
Node *w = min;
Node *end = w->left;
bool flag = true;
while(flag)
{
if(w == end)
flag = false;
Node *x = w;
size_t d = x->degree;
while(A[d] != nil)
{
Node *y = A[d];
if(x->key > y->key)
{
int temp = x->key;
x->key = y->key;
y->key = temp;
}
link(x,y);
A[d] = nil;
if(d+1 != size)
d += 1;
else
break;
}
A[d] = x;
w = w->right;
}
min = w;
for(size_t i = 0; i != size; ++i)
{
if(A[i] != nil && min->key > A[i]->key)
min = A[i];
}
::operator delete[](static_cast(A));
}
void FibHeap::link(Node *x,Node *y)//consolidDate辅助函数,将y从根列表移除,合并到x上
{
Node *left = y->left;
Node *right = y->right;
left->right = right;
right->left = left;
y->left = y;
y->right = y;
Node *childs = x->c;
x->c = y;
y->p = x;
if(childs != nil)
{
Node *b = childs->left;
childs->left = y;
b->right = y;
y->right = childs;
y->left = b;
}
x->degree += 1;
y->mark = false;
}
void FibHeap::cut(Node *x,Node *y)
{
Node *left = x->left;
Node *parent = x->p;
Node *right = x->right;
left->right = right;
right->left = left;
x->left = nil;
x->right = nil;
x->p = nil;
if(parent->c == x && left != x)
{
parent->c = left;
y->degree -= 1;
}
if(parent->c == x && left == x)
parent->c = nil;
insert(x);
num -= 1;
x->mark = false;
}
void FibHeap::cascadingCut(Node *y)
{
Node *z = y->p;
if(z != nil)
{
if(y->mark == false)
y->mark =true;
else
{
cut(y,z);
cascadingCut(z);
}
}
}
}
第一篇博文,有很多很生疏的地方,姑且自己娱乐自己吧。
实现的也有不少问题,算法导论上对于斐波那契堆的应用讲得并不多,有时候都会想,这么纠结的玩意,到哪去应用呢?
有些操作,书上都是随意带过了,但是自己实现的时候还是出现了许多问题,如果大神路过,请轻喷。
同二项堆不同,其中的树并不一定是二项树,而是最小堆有序树。
这是一种基于平摊分析的产物,尽可能将一些操作延后至extractMin操作。
复杂度高,但是平摊时间界却很好,对它的使用有一定取舍,可以适应于一些应用。
两天时间,写了两遍,对自己很不满意。
后续有时间,肯定要优化下,不过现在,脑子已经快罢工了。
贴上最后的main。
#include "fheap.h"
#include
using fib::FibHeap;
using fib::Node;
void main()
{
FibHeap fh;
FibHeap fh2;
Node n1(10);
Node n2(9);
Node n3(8);
Node n4(7);
Node n5(6);
Node n6(5);
fh.insert(&n1);
fh.insert(&n2);
fh.insert(&n3);
fh2.insert(&n4);
fh2.insert(&n5);
fh2.insert(&n6);
fh.heapUnion(&fh2);
Node *min = fh.extractMin();
fh.decreaseKey(&n1,2);
fh.del(&n1);
system("Pause");
}
float mySqrt(int x)
{
//第一步寻找近似值
int y = 0;
while( (y+1)*(y+1) < x)
++y;
if( (y+1)*(y+1) == x)
return float(y+1);
float z = float(y);
size_t num = 3;//计算次数
while(num--)
{
z = (z+x/z)/2;
}
return z;
}
这种方法的原理通过逐步进行近似计算,取到想要的精度即可。
例如对于5,可以找到第一个近似值2^2,不大于5。
然后通过 z = (z+x/z)/2的方法,一直进行计算,z是上一步的近似计算值(貌似就是牛顿迭代的变形?)
还有诸如二分法之类的也可以,网上瞥到了牛顿迭代法(其实我只知道名字,没细看),等等等等。
只是一个小插曲,以后有机会可以继续研究。