斐波那契堆
主要参考《算法导论》
1、斐波那契堆的结构
与二项堆一样,斐波那契堆是由一组最小堆的有序树构成,但堆中的树并不一定是二项树。与二项堆中树都是有序的不同,斐波那契堆中的树都是有根而无序的。
上图是一个由5棵最小堆有序树和14个结点构成的一个斐波那契堆。(A Fibonacci heap consisting of five min-heap-orderd trees and 14 nodes)
斐波那契堆的表示(实现)
typedef struct fhnode {
int key;
int degree; // the number of children
bool mark; // whether node x has lost a child
struct fhnode *parent;
struct fhnode *child;
struct fhnode *left;
struct fhnode *right;
} FHNode;
typedef struct fibheap {
int n; // the number of nodes currently in H
struct fhnode *min; // point to the root of a tree containing the minimum key
} *FHeap;
2、斐波那契堆的操作
1)创建一个新的斐波那契堆
//=============================================================
// 创建一个空堆
//=============================================================
FHeap FibHeapCreate(/*FHeap H*/)
{
FHeap H = (struct fibheap*)malloc(sizeof(struct fibheap));
if (!H) {
printf("H malloc error\n");
return NULL;
}
H->min = NULL;
H->n = 0;
return H;
}
2)将结点x插入斐波那契堆中
//=============================================================
// 将x结点插到cur结点前面(使用时,保证cur存在,即cur!=NULL)
//=============================================================
static inline void InsertNode(FHNode *x, FHNode *cur)
{
cur->left->right = x;
x->left = cur->left;
x->right = cur;
cur->left = x;
}
//=============================================================
// 将x结点从链表中移走
//=============================================================
static inline void RemoveNode(FHNode *x)
{
x->left->right = x->right;
x->right->left = x->left;
}
//=============================================================
// 将含key值的结点插入堆中
//=============================================================
FHeap FibHeapInsert(FHeap H, int key)
{
// 初始化新结点
FHNode *x = (FHNode *)malloc(sizeof(FHNode));
if (!x) {
printf("x malloc error\n");
return NULL;
}
x->key = key;
x->degree = 0;
x->parent = NULL;
x->child = NULL;
x->left = x->right = x;
x->mark = false;
if (H->min == NULL) { // 插入第一个结点
H->min = x;
} else {
InsertNode(x, H->min); // 在根链表中插入x结点
if (x->key < H->min->key)
H->min = x;
}
H->n += 1;
}
3)寻找最小结点
//=============================================================
// 获得堆中key值最小的结点
//=============================================================
FHNode *FibHeapMinimum(FHeap H)
{
return H->min;
}
4)合并两个斐波那契堆中
该过程只是简单的将斐波那契堆H1和H2的两个根链表进行合并,然后确定一个新的最小结点。
//=============================================================
// 将堆H1与H2进行合并
//=============================================================
FHeap FibHeapUnion(FHeap H1, FHeap H2)
{
FHeap H = FibHeapCreate();
H->min = H1->min;
FHNode *x = H->min; // concatenate the root list of H2 with the root list of H
FHNode *y = H2->min;
if (x != NULL && y != NULL) {
FHNode *xl = x->left;
FHNode *yl= y->left;
xl->right = y;
y->left = xl;
yl->right = x;
x->left = yl;
}
// 确定H->min的位置
if (H1->min == NULL || (H2->min != NULL && H2->min->key < H1->min->key))
H->min = H2->min;
H->n = H1->n + H2->n;
return H;
}
5)抽取最小结点
该操作会调用一个consolidate过程来对根链表中的树进行合并。
过程consolidate会使用一个辅助数组A[D(H.n)],并反复执行下面的步骤:
a. 在跟链表中找出两个具有相同度数的根x和y,且x.key <= y.key;
b. 将y链接到x:将y从跟链表中移出,并让其成为x的孩子。
直到根链表中的每个根都有不同的degree值。
//=============================================================
// 将y结点插入到x的孩子链表中
//=============================================================
static inline void FibHeapLink(FHeap H, FHNode *y, FHNode *x)
{
RemoveNode(y);
if (!x->child) { // x的孩子不存在
x->child = y;
y->left = y->right = y;
} else {
InsertNode(y, x->child);
}
x->degree += 1;
y->parent = x;
y->mark = false;
}
//=============================================================
// 获得堆的最大度d = lg(n)
//=============================================================
static inline int GetMaxDegree(double n)
{
return log(n) / log(1.618);
}
//=============================================================
// 用于交换x与y
//=============================================================
static inline void SwapNode(FHNode **x, FHNode **y)
{
FHNode *tmp = *x;
*x = *y;
*y = tmp;
}
//=============================================================
// 合并根链表,减少堆中树的数目
//=============================================================
void Consolidate(FHeap H)
{
// 构建数组A,用于根结点的合并
int md = GetMaxDegree(H->n);
FHNode **A = (FHNode **)malloc(sizeof(FHNode *) * (md + 1));
if (!A) {
printf("A malloc error\n");
return ;
}
for (int i = 0; i <= md; i++)
A[i] = NULL;
FHNode *w = H->min;
FHNode *last = NULL; // 用作结束的标记
// 遍历根链表,找到度相同的两个树进行合并
// 这里坑较多,写代码时,一定要小心
if (w)
last = w->left; // 记录结束点
while (w) {
FHNode *next = w->right; // 临时保存,用于迭代
FHNode *x = w;
int d = x->degree;
while (d <= md && A[d] != NULL) {
FHNode *y = A[d];
if (x->key > y->key) { // x与y进行交换,保证x为根
SwapNode(&x, &y);
}
FibHeapLink(H, y, x); // x度数也加了1
A[d] = NULL;
d += 1;
}
A[d] = x;
if (w == last) // 循环结束
break;
w = next;
}
// 根据A来重新构建堆,感觉遍历找到H->min最更省事
H->min = NULL;
for (int i = 0; i <= md; i++) {
if (A[i] != NULL) {
RemoveNode(A[i]);
if (H->min == NULL) {
A[i]->left = A[i]->right = A[i];
H->min = A[i];
} else {
InsertNode(A[i], H->min);
if (A[i]->key < H->min->key)
H->min = A[i];
}
}
}
}
//=============================================================
// 提取堆中key值最小的结点
//=============================================================
FHNode *FibHeapExtractMin(FHeap H)
{
FHNode *z = H->min;
if (z != NULL) {
FHNode *cp = z->child; // 将z的孩子插入根链表中
while (z->degree--) {
FHNode *next = cp->right;
RemoveNode(cp); // 先将cp从孩子链表中移除
cp->parent = NULL;
InsertNode(cp, z); // 再插入到根链表中
cp = next;
}
// 合并根链表中的树
if (z == z->right) {// 只有一个结点
H->min = NULL;
} else {
H->min = z->right;
RemoveNode(z); // 将z从跟链表中移除
Consolidate(H);
}
H->n -= 1;
}
return z;
}
6)减少一个关键字
这里实现起来较为容易,但理解起来有点难(尤其是mark值)。
//=============================================================
// 将y的孩子x插入到根链表中(切除x)
//=============================================================
static inline void Cut(FHeap H, FHNode *x, FHNode *y)
{
RemoveNode(x);
y->degree -= 1;
InsertNode(x, H->min);
x->parent = NULL;
x->mark = false;
}
//=============================================================
// 级联切除父结点
//=============================================================
static inline void CascadingCut(FHeap H, FHNode *y)
{
FHNode *z = y->parent;
if (z) {
if (y->mark == false)
y->mark = true;
else {
Cut(H, y, z);
CascadingCut(H, z);
}
}
}
//=============================================================
// 将x结点中的x->key值降至key(x->key < key)
//=============================================================
void FibHeapDecreaseKey(FHeap H, FHNode *x, int key)
{
if (key > x->key) {
printf("new key is greater than current key\n");
return ;
}
x->key = key;
FHNode *y = x->parent;
if (y && x->key < y->key) { // 考虑x非根结点,及(key < x->parent->key)情况
Cut(H, x, y);
CascadingCut(H, y);
}
if (x->key < H->min->key)
H->min = x;
}
7)删除一个结点
//=============================================================
// 将x结点从堆中删除
//=============================================================
void FibHeapDelete(FHeap H, FHNode *x)
{
FibHeapDecreaseKey(H, x, MINVAL);
FibHeapExtractMin(H);
}
下面是程序的完整代码
//===============================================================
// 斐波那契堆的实现
// 时间:2014/8/2
//===============================================================
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#define MINVAL -0xffff
typedef struct fhnode {
int key;
int degree; // the number of children
bool mark; // whether node x has lost a child
struct fhnode *parent;
struct fhnode *child;
struct fhnode *left;
struct fhnode *right;
} FHNode;
typedef struct fibheap {
int n; // the number of nodes currently in H
struct fhnode *min; // point to the root of a tree containing the minimum key
} *FHeap;
//=============================================================
// 创建一个空堆
//=============================================================
FHeap FibHeapCreate(/*FHeap H*/)
{
FHeap H = (struct fibheap*)malloc(sizeof(struct fibheap));
if (!H) {
printf("H malloc error\n");
return NULL;
}
H->min = NULL;
H->n = 0;
return H;
}
//=============================================================
// 将x结点插到cur结点前面(使用时,保证cur存在,即cur!=NULL)
//=============================================================
static inline void InsertNode(FHNode *x, FHNode *cur)
{
cur->left->right = x;
x->left = cur->left;
x->right = cur;
cur->left = x;
}
//=============================================================
// 将x结点从链表中移走
//=============================================================
static inline void RemoveNode(FHNode *x)
{
x->left->right = x->right;
x->right->left = x->left;
}
//=============================================================
// 将含key值的结点插入堆中
//=============================================================
FHeap FibHeapInsert(FHeap H, int key)
{
// 初始化新结点
FHNode *x = (FHNode *)malloc(sizeof(FHNode));
if (!x) {
printf("x malloc error\n");
return NULL;
}
x->key = key;
x->degree = 0;
x->parent = NULL;
x->child = NULL;
x->left = x->right = x;
x->mark = false;
if (H->min == NULL) { // 插入第一个结点
H->min = x;
} else {
InsertNode(x, H->min); // 在根链表中插入x结点
if (x->key < H->min->key)
H->min = x;
}
H->n += 1;
}
//=============================================================
// 获得堆中key值最小的结点
//=============================================================
FHNode *FibHeapMinimum(FHeap H)
{
return H->min;
}
//=============================================================
// 将堆H1与H2进行合并
//=============================================================
FHeap FibHeapUnion(FHeap H1, FHeap H2)
{
FHeap H = FibHeapCreate();
H->min = H1->min;
// FHNode *x = H->min;
// FHNode *y = H2->min;
// FHNode *yl = y->left;
// x->left->right = y;
// y->left = x->left;
// x->left = yl;
// yl->right = x;
FHNode *x = H->min; // concatenate the root list of H2 with the root list of H
FHNode *y = H2->min;
if (x != NULL && y != NULL) {
FHNode *xl = x->left;
FHNode *yl= y->left;
xl->right = y;
y->left = xl;
yl->right = x;
x->left = yl;
}
// 确定H->min的位置
if (H1->min == NULL || (H2->min != NULL && H2->min->key < H1->min->key))
H->min = H2->min;
H->n = H1->n + H2->n;
return H;
}
//=============================================================
// 将y结点插入到x的孩子链表中
//=============================================================
static inline void FibHeapLink(FHeap H, FHNode *y, FHNode *x)
{
RemoveNode(y);
if (!x->child) { // x的孩子不存在
x->child = y;
y->left = y->right = y;
} else {
InsertNode(y, x->child);
}
x->degree += 1;
y->parent = x;
y->mark = false;
}
//=============================================================
// 获得堆的最大度d = lg(n)
//=============================================================
static inline int GetMaxDegree(double n)
{
return log(n) / log(1.618);
}
//=============================================================
// 用于交换x与y
//=============================================================
static inline void SwapNode(FHNode **x, FHNode **y)
{
FHNode *tmp = *x;
*x = *y;
*y = tmp;
}
//=============================================================
// 合并根链表,减少堆中树的数目
//=============================================================
void Consolidate(FHeap H)
{
// 构建数组A,用于根结点的合并
int md = GetMaxDegree(H->n);
FHNode **A = (FHNode **)malloc(sizeof(FHNode *) * (md + 1));
if (!A) {
printf("A malloc error\n");
return ;
}
for (int i = 0; i <= md; i++)
A[i] = NULL;
FHNode *w = H->min;
FHNode *last = NULL; // 用作结束的标记
// 遍历根链表,找到度相同的两个树进行合并
// 这里坑较多,写代码时,一定要小心
if (w)
last = w->left; // 记录结束点
while (w) {
FHNode *next = w->right; // 临时保存,用于迭代
FHNode *x = w;
int d = x->degree;
while (d <= md && A[d] != NULL) {
FHNode *y = A[d];
if (x->key > y->key) { // x与y进行交换,保证x为根
SwapNode(&x, &y);
}
FibHeapLink(H, y, x); // x度数也加了1
A[d] = NULL;
d += 1;
}
A[d] = x;
if (w == last) // 循环结束
break;
w = next;
}
// 根据A来重新构建堆,感觉遍历找到H->min最更省事
H->min = NULL;
for (int i = 0; i <= md; i++) {
if (A[i] != NULL) {
RemoveNode(A[i]);
if (H->min == NULL) {
A[i]->left = A[i]->right = A[i];
H->min = A[i];
} else {
InsertNode(A[i], H->min);
if (A[i]->key < H->min->key)
H->min = A[i];
}
}
}
}
//=============================================================
// 提取堆中key值最小的结点
//=============================================================
FHNode *FibHeapExtractMin(FHeap H)
{
FHNode *z = H->min;
if (z != NULL) {
FHNode *cp = z->child; // 将z的孩子插入根链表中
while (z->degree--) {
FHNode *next = cp->right;
RemoveNode(cp); // 先将cp从孩子链表中移除
cp->parent = NULL;
InsertNode(cp, z); // 再插入到根链表中
cp = next;
}
// 合并根链表中的树
if (z == z->right) {// 只有一个结点
H->min = NULL;
} else {
H->min = z->right;
RemoveNode(z); // 将z从跟链表中移除
Consolidate(H);
}
H->n -= 1;
}
return z;
}
//=============================================================
// 将y的孩子x插入到根链表中(切除x)
//=============================================================
static inline void Cut(FHeap H, FHNode *x, FHNode *y)
{
RemoveNode(x);
y->degree -= 1;
InsertNode(x, H->min);
x->parent = NULL;
x->mark = false;
}
//=============================================================
// 级联切除父结点
//=============================================================
static inline void CascadingCut(FHeap H, FHNode *y)
{
FHNode *z = y->parent;
if (z) {
if (y->mark == false)
y->mark = true;
else {
Cut(H, y, z);
CascadingCut(H, z);
}
}
}
//=============================================================
// 将x结点中的x->key值降至key(x->key < key)
//=============================================================
void FibHeapDecreaseKey(FHeap H, FHNode *x, int key)
{
if (key > x->key) {
printf("new key is greater than current key\n");
return ;
}
x->key = key;
FHNode *y = x->parent;
if (y && x->key < y->key) { // 考虑x非根结点,及(key < x->parent->key)情况
Cut(H, x, y);
CascadingCut(H, y);
}
if (x->key < H->min->key)
H->min = x;
}
//=============================================================
// 将x结点从堆中删除
//=============================================================
void FibHeapDelete(FHeap H, FHNode *x)
{
FibHeapDecreaseKey(H, x, MINVAL);
FibHeapExtractMin(H);
}
//=============================================================
// 在堆中查找包含key的结点
//=============================================================
FHNode *FibHeapSearch(FHNode *x, int key)
{
FHNode *pos = x;
FHNode *y = NULL;
if (pos) {
do {
if (pos->key == key)
return pos;
else if (pos->child)
y = FibHeapSearch(pos->child, key);
pos = pos->right;
} while (pos != x);
}
return y;
}
//=============================================================
// 打印堆中所有结点的key值
//=============================================================
void FibHeapPrint(FHNode *x)
{
FHNode *pos = x;
if (pos) {
do {
printf("%d\t", pos->key);
if (pos->child) {
FibHeapPrint(pos->child);
}
pos = pos->right;
} while (pos != x);
}
}
//=============================================================
// 从pos处开始打印循环双链表中的key值(调试时使用)
//=============================================================
void PrintDList(FHNode *pos)
{
FHNode *cur = pos;
if (cur) {
printf("%d\t", cur->key);
cur = cur->right;
}
while (cur != pos) {
printf("%d\t", cur->key);
cur = cur->right;
}
// if (cur) {
// do {
// printf("%d\t", cur->key);
// cur = cur->right;
// } while (cur != pos);
// }
//
// if (cur) {
// while (cur->right != pos) {
// printf("%d\t", cur->key);
// cur = cur->right;
// }
// if (cur) { // 打印最后一个结点
// printf("%d\t", cur->key);
// }
// }
printf("\n");
}
int main()
{
int arr[] = {45, 65, 23, 9, 6, 21, 97, 60, 23, 7, 90, 61, 123, 987, 51, 11}; // 9
int len = sizeof(arr) / sizeof(arr[0]);
FHeap H1 = FibHeapCreate();
for (int i = 0; i < len - 5; i++) {
printf("H1 insert:%d\n", arr[i]);
FibHeapInsert(H1, arr[i]);
}
printf("\n");
FHeap H2 = FibHeapCreate();
for (int i = len - 5; i < len; i++) {
printf("H2 insert:%d\n", arr[i]);
FibHeapInsert(H2, arr[i]);
}
printf("\n");
printf("Union:");
FHeap H = FibHeapUnion(H1, H2);
printf("PrintKey:\n");
FibHeapPrint(H->min);
printf("\n");
printf("ExtractMinKey:\n");
/*for (int i = 0; i < len; i++)*/ {
FHNode *min = FibHeapExtractMin(H);
printf("%d\n", min->key);
}
printf("PrintKey:\n");
FibHeapPrint(H->min);
printf("\n");
printf("Search:\n");
FHNode *x = FibHeapSearch(H->min, 23);
printf("%d\n", x->key);
x = FibHeapSearch(H->min, 60);
printf("%d\n", x->key);
printf("DecreaseKey: %d-->3\n", x->key);
FibHeapDecreaseKey(H, x, 3);
printf("MinimumKey: ");
FHNode *min = FibHeapMinimum(H);
printf("%d\n", min->key);
printf("DeleteKey: %d\n", min->key);
FibHeapDelete(H, min);
printf("ExtractMinKey: ");
min = FibHeapExtractMin(H);
printf("%d\n", min->key);
system("pause");
return 0;
}