出自:http://www.cnblogs.com/skywang12345/p/3659060.html
概要
本章介绍斐波那契堆。和以往一样,本文会先对斐波那契堆的理论知识进行简单介绍,然后给出C语言的实现。后续再分别给出C++和Java版本的实现;实现的语言虽不同,但是原理如出一辙,选择其中之一进行了解即可。若文章有错误或不足的地方,请不吝指出!
目录
1. 斐波那契堆的介绍
2. 斐波那契堆的基本操作
3. 斐波那契堆的C实现(完整源码)
4. 斐波那契堆的C测试程序
转载请注明出处:http://www.cnblogs.com/skywang12345/p/3659060.html
更多内容:数据结构与算法系列 目录
(01) 斐波那契堆(一)之 图文解析 和 C语言的实现
(02) 斐波那契堆(二)之 C++的实现
(03) 斐波那契堆(三)之 Java的实现
斐波那契堆的介绍
斐波那契堆(Fibonacci heap)是堆中一种,它和二项堆一样,也是一种可合并堆;可用于实现合并优先队列。斐波那契堆比二项堆具有更好的平摊分析性能,它的合并操作的时间复杂度是O(1)。
与二项堆一样,它也是由一组堆最小有序树组成,并且是一种可合并堆。
与二项堆不同的是,斐波那契堆中的树不一定是二项树;而且二项堆中的树是有序排列的,但是斐波那契堆中的树都是有根而无序的。
斐波那契堆的基本操作
1. 基本定义
typedef int Type;typedef struct _FibonacciNode {Type key; // 关键字(键值)int degree; // 度数struct _FibonacciNode *left; // 左兄弟struct _FibonacciNode *right; // 右兄弟struct _FibonacciNode *child; // 第一个孩子节点struct _FibonacciNode *parent; // 父节点int marked; //是否被删除第1个孩子(1表示删除,0表示未删除) }FibonacciNode, FibNode;
FibNode是斐波那契堆的节点类,它包含的信息较多。key是用于比较节点大小的,degree是记录节点的度,left和right分别是指向节点的左右兄弟,child是节点的第一个孩子,parent是节点的父节点,marked是记录该节点是否被删除第1个孩子(marked在删除节点时有用)。
typedef struct _FibonacciHeap{int keyNum; // 堆中节点的总数int maxDegree; // 最大度struct _FibonacciNode *min; // 最小节点(某个最小堆的根节点)struct _FibonacciNode **cons; // 最大度的内存区域 }FibonacciHeap, FibHeap;
FibHeap是斐波那契堆对应的类。min是保存当前堆的最小节点,keyNum用于记录堆中节点的总数,maxDegree用于记录堆中最大度,而cons在删除节点时来暂时保存堆数据的临时空间。
下面看看斐波那契堆的内存结构图。
从图中可以看出,斐波那契堆是由一组最小堆组成,这些最小堆的根节点组成了双向链表(后文称为"根链表");斐波那契堆中的最小节点就是"根链表中的最小节点"!
PS. 上面这幅图的结构和测试代码中的"基本信息"测试函数的结果是一致的;你可以通过测试程序来亲自验证!
2. 插入操作
插入操作非常简单:插入一个节点到堆中,直接将该节点插入到"根链表的min节点"之前即可;若被插入节点比"min节点"小,则更新"min节点"为被插入节点。
上面是插入操作的示意图。
斐波那契堆的根链表是"双向链表",这里将min节点看作双向联表的表头(后文也是如此)。在插入节点时,每次都是"将节点插入到min节点之前(即插入到双链表末尾)"。此外,对于根链表中最小堆都只有一个节点的情况,插入操作就很演化成双向链表的插入操作。
此外,插入操作示意图与测试程序中的"插入操作"相对应,感兴趣的可以亲自验证。
插入操作代码
/** 将"单个节点node"加入"链表root"之前* a …… root* a …… node …… root** 注意: 此处node是单个节点,而root是双向链表 */ static void fib_node_add(FibNode *node, FibNode *root) {node->left = root->left;root->left->right = node;node->right = root;root->left = node; }/** 将节点node插入到斐波那契堆heap中*/ static void fib_heap_insert_node(FibHeap *heap, FibNode *node) {if (heap->keyNum == 0)heap->min = node;else{fib_node_add(node, heap->min);if (node->key < heap->min->key)heap->min = node;}heap->keyNum++; }
3. 合并操作
合并操作和插入操作的原理非常类似:将一个堆的根链表插入到另一个堆的根链表上即可。简单来说,就是将两个双链表拼接成一个双向链表。
上面是合并操作的示意图。该操作示意图与测试程序中的"合并操作"相对应!
合并操作代码
/** 将双向链表b链接到双向链表a的后面** 注意: 此处a和b都是双向链表 */ static void fib_node_cat(FibNode *a, FibNode *b) {FibNode *tmp;tmp = a->right;a->right = b->right;b->right->left = a;b->right = tmp;tmp->left = b; }/** 将h1, h2合并成一个堆,并返回合并后的堆*/ FibHeap* fib_heap_union(FibHeap *h1, FibHeap *h2) {FibHeap *tmp;if (h1==NULL)return h2;if (h2==NULL)return h1;// 以h1为"母",将h2附加到h1上;下面是保证h1的度数大,尽可能的少操作。if(h2->maxDegree > h1->maxDegree){tmp = h1;h1 = h2;h2 = tmp;}if((h1->min) == NULL) // h1无"最小节点" {h1->min = h2->min;h1->keyNum = h2->keyNum;free(h2->cons);free(h2);}else if((h2->min) == NULL) // h1有"最小节点" && h2无"最小节点" {free(h2->cons);free(h2);} // h1有"最小节点" && h2有"最小节点"else{// 将"h2中根链表"添加到"h1"中fib_node_cat(h1->min, h2->min);if (h1->min->key > h2->min->key)h1->min = h2->min;h1->keyNum += h2->keyNum;free(h2->cons);free(h2);}return h1; }
4. 取出最小节点
抽取最小结点的操作是斐波那契堆中较复杂的操作。
(1)将要抽取最小结点的子树都直接串联在根表中;
(2)合并所有degree相等的树,直到没有相等的degree的树。
上面是取出最小节点的示意图。图中应该写的非常明白了,若有疑问,看代码。
此外,该操作示意图与测试程序中的"删除最小节点"相对应!有兴趣的可以亲自验证。
取出最小节点代码
/** 移除最小节点,并返回移除节点后的斐波那契堆*/ FibNode* _fib_heap_extract_min(FibHeap *heap) {if (heap==NULL || heap->min==NULL)return NULL;FibNode *child = NULL;FibNode *min = heap->min;// 将min每一个儿子(儿子和儿子的兄弟)都添加到"斐波那契堆的根链表"中while (min->child != NULL){child = min->child;fib_node_remove(child);if (child->right == child)min->child = NULL;elsemin->child = child->right;fib_node_add(child, heap->min);child->parent = NULL;}// 将min从根链表中移除 fib_node_remove(min);// 若min是堆中唯一节点,则设置堆的最小节点为NULL;// 否则,设置堆的最小节点为一个非空节点(min->right),然后再进行调节。if (min->right == min)heap->min = NULL;else{heap->min = min->right;fib_heap_consolidate(heap);}heap->keyNum--;return min; }
其中fib_heap_consolidate(heap)的作用是合并斐波那契堆的根链表中相同度数的树,它的相关代码如下:

1 /* 2 * 将node链接到root根结点 3 */ 4 static void fib_heap_link(FibHeap * heap, FibNode * node, FibNode *root) 5 { 6 // 将node从双链表中移除 7 fib_node_remove(node); 8 // 将node设为root的孩子 9 if (root->child == NULL) 10 root->child = node; 11 else 12 fib_node_add(node, root->child); 13 14 node->parent = root; 15 root->degree++; 16 node->marked = 0; 17 } 18 19 /* 20 * 创建fib_heap_consolidate所需空间 21 */ 22 static void fib_heap_cons_make(FibHeap * heap) 23 { 24 int old = heap->maxDegree; 25 26 // 计算log2(x),"+1"意味着向上取整! 27 // ex. log2(13) = 3,向上取整为3+1=4。 28 heap->maxDegree = LOG2(heap->keyNum) + 1; 29 30 // 如果原本空间不够,则再次分配内存 31 if (old >= heap->maxDegree) 32 return ; 33 34 // 因为度为heap->maxDegree可能被合并,所以要maxDegree+1 35 heap->cons = (FibNode **)realloc(heap->cons, 36 sizeof(FibHeap *) * (heap->maxDegree + 1)); 37 } 38 39 /* 40 * 合并斐波那契堆的根链表中左右相同度数的树 41 */ 42 static void fib_heap_consolidate(FibHeap *heap) 43 { 44 int i, d, D; 45 FibNode *x, *y, *tmp; 46 47 fib_heap_cons_make(heap);//开辟哈希所用空间 48 D = heap->maxDegree + 1; 49 50 for (i = 0; i < D; i++) 51 heap->cons[i] = NULL; 52 53 // 合并相同度的根节点,使每个度数的树唯一 54 while (heap->min != NULL) 55 { 56 x = fib_heap_remove_min(heap); // 取出堆中的最小树(最小节点所在的树) 57 d = x->degree; // 获取最小树的度数 58 // heap->cons[d] != NULL,意味着有两棵树(x和y)的"度数"相同。 59 while (heap->cons[d] != NULL) 60 { 61 y = heap->cons[d]; // y是"与x的度数相同的树" 62 if (x->key > y->key) // 保证x的键值比y小 63 { 64 tmp = x; 65 x = y; 66 y = tmp; 67 68 } 69 fib_heap_link(heap, y, x); // 将y链接到x中 70 heap->cons[d] = NULL; 71 d++; 72 } 73 heap->cons[d] = x; 74 } 75 heap->min = NULL; 76 77 // 将heap->cons中的结点重新加到根表中 78 for (i=0; i<D; i++) 79 { 80 if (heap->cons[i] != NULL) 81 { 82 if (heap->min == NULL) 83 heap->min = heap->cons[i]; 84 else 85 { 86 fib_node_add(heap->cons[i], heap->min); 87 if ((heap->cons[i])->key < heap->min->key) 88 heap->min = heap->cons[i]; 89 } 90 } 91 } 92 }
5. 减小节点值
减少斐波那契堆中的节点的键值,这个操作的难点是:如果减少节点后破坏了"最小堆"性质,如何去维护呢?下面对一般性情况进行分析。
(1) 首先,将"被减小节点"从"它所在的最小堆"剥离出来;然后将"该节点"关联到"根链表"中。 倘若被减小的节点不是单独一个节点,而是包含子树的树根。则是将以"被减小节点"为根的子树从"最小堆"中剥离出来,然后将该树关联到根链表中。
(2) 接着,对"被减少节点"的原父节点进行"级联剪切"。所谓"级联剪切",就是在被减小节点破坏了最小堆性质,并被切下来之后;再从"它的父节点"进行递归级联剪切操作。
而级联操作的具体动作则是:若父节点(被减小节点的父节点)的marked标记为false,则将其设为true,然后退出。
否则,将父节点从最小堆中切下来(方式和"切被减小节点的方式"一样);然后递归对祖父节点进行"级联剪切"。
marked标记的作用就是用来标记"该节点的子节点是否有被删除过",它的作用是来实现级联剪切。而级联剪切的真正目的是为了防止"最小堆"由二叉树演化成链表。
(3) 最后,别忘了对根链表的最小节点进行更新。
上面是减小节点值的示意图。该操作示意图与测试程序中的"减小节点"相对应!
减小节点值的代码
/* * 将斐波那契堆heap中节点node的值减少为key*/ static void fib_heap_decrease(FibHeap *heap, FibNode *node, Type key) {FibNode *parent;if (heap==NULL || heap->min==NULL ||node==NULL) return ;if ( key>=node->key){printf("decrease failed: the new key(%d) is no smaller than current key(%d)\n", key, node->key);return ;}node->key = key;parent = node->parent;if (parent!=NULL && node->key < parent->key){// 将node从父节点parent中剥离出来,并将node添加到根链表中 fib_heap_cut(heap, node, parent);fib_heap_cascading_cut(heap, parent);}// 更新最小节点if (node->key < heap->min->key)heap->min = node; }
其中,fib_heap_cut()和fib_heap_cascading_cut()的相关代码如下:

1 /* 2 * 将node从父节点parent的子链接中剥离出来, 3 * 并使node成为"堆的根链表"中的一员。 4 */ 5 static void fib_heap_cut(FibHeap *heap, FibNode *node, FibNode *parent) 6 { 7 fib_node_remove(node); 8 renew_degree(parent, node->degree); 9 // node没有兄弟 10 if (node == node->right) 11 parent->child = NULL; 12 else 13 parent->child = node->right; 14 15 node->parent = NULL; 16 node->left = node->right = node; 17 node->marked = 0; 18 // 将"node所在树"添加到"根链表"中 19 fib_node_add(node, heap->min); 20 } 21 22 /* 23 * 对节点node进行"级联剪切" 24 * 25 * 级联剪切:如果减小后的结点破坏了最小堆性质, 26 * 则把它切下来(即从所在双向链表中删除,并将 27 * 其插入到由最小树根节点形成的双向链表中), 28 * 然后再从"被切节点的父节点"到所在树根节点递归执行级联剪枝 29 */ 30 static void fib_heap_cascading_cut(FibHeap *heap, FibNode *node) 31 { 32 FibNode *parent = node->parent; 33 if (parent != NULL) 34 return ; 35 36 if (node->marked == 0) 37 node->marked = 1; 38 else 39 { 40 fib_heap_cut(heap, node, parent); 41 fib_heap_cascading_cut(heap, parent); 42 } 43 }
6. 增加节点值
增加节点值和减少节点值类似,这个操作的难点也是如何维护"最小堆"性质。思路如下:
(1) 将"被增加节点"的"左孩子和左孩子的所有兄弟"都链接到根链表中。
(2) 接下来,把"被增加节点"添加到根链表;但是别忘了对其进行级联剪切。
上面是增加节点值的示意图。该操作示意图与测试程序中的"增大节点"相对应!
增加节点值的代码
/* * 将斐波那契堆heap中节点node的值增加为key*/ static void fib_heap_increase(FibHeap *heap, FibNode *node, Type key) {FibNode *child, *parent, *right;if (heap==NULL || heap->min==NULL ||node==NULL) return ;if (key <= node->key){printf("increase failed: the new key(%d) is no greater than current key(%d)\n", key, node->key);return ;}// 将node每一个儿子(不包括孙子,重孙,...)都添加到"斐波那契堆的根链表"中while (node->child != NULL){child = node->child;fib_node_remove(child); // 将child从node的子链表中删除if (child->right == child)node->child = NULL;elsenode->child = child->right;fib_node_add(child, heap->min); // 将child添加到根链表中child->parent = NULL;}node->degree = 0;node->key = key;// 如果node不在根链表中,// 则将node从父节点parent的子链接中剥离出来,// 并使node成为"堆的根链表"中的一员,// 然后进行"级联剪切"// 否则,则判断是否需要更新堆的最小节点parent = node->parent;if(parent != NULL){fib_heap_cut(heap, node, parent);fib_heap_cascading_cut(heap, parent);}else if(heap->min == node){right = node->right;while(right != node){if(node->key > right->key)heap->min = right;right = right->right;}} }
7. 删除节点
删除节点,本文采用了操作是:"取出最小节点"和"减小节点值"的组合。
(1) 先将被删除节点的键值减少。减少后的值要比"原最小节点的值"即可。
(2) 接着,取出最小节点即可。
删除节点值的代码
/** 删除结点node*/ static void _fib_heap_delete(FibHeap *heap, FibNode *node) {Type min = heap->min->key;fib_heap_decrease(heap, node, min-1);_fib_heap_extract_min(heap);free(node); }
注意:关于斐波那契堆的"更新"、"打印"、"销毁"等接口就不再单独介绍了。后文的源码中有给出它们的实现代码,Please RTFSC(Read The Fucking Source Code)!
斐波那契堆的C实现(完整源码)
斐波那契堆的头文件(fibonacci_heap.h)

1 #ifndef _FIBONACCI_HEAP_H_ 2 #define _FIBONACCI_HEAP_H_ 3 4 typedef int Type; 5 6 typedef struct _FibonacciNode 7 { 8 Type key; // 关键字(键值) 9 int degree; // 度数 10 struct _FibonacciNode *left; // 左兄弟 11 struct _FibonacciNode *right; // 右兄弟 12 struct _FibonacciNode *child; // 第一个孩子节点 13 struct _FibonacciNode *parent; // 父节点 14 int marked; //是否被删除第1个孩子(1表示删除,0表示未删除) 15 }FibonacciNode, FibNode; 16 17 typedef struct _FibonacciHeap{ 18 int keyNum; // 堆中节点的总数 19 int maxDegree; // 最大度 20 struct _FibonacciNode *min; // 最小节点(某个最小堆的根节点) 21 struct _FibonacciNode **cons; // 最大度的内存区域 22 }FibonacciHeap, FibHeap; 23 24 // 创建Fibonacci堆 25 FibHeap* fib_heap_make(); 26 // 新建键值为key的节点,并将其插入到斐波那契堆中 27 void fib_heap_insert_key(FibHeap *heap, Type key); 28 // 删除键值为key的结点 29 void fib_heap_delete(FibHeap *heap, Type key); 30 // 移除最小节点 31 void fib_heap_extract_min(FibHeap *heap); 32 // 更新heap的中的oldkey为newkey 33 void fib_heap_update(FibHeap *heap, Type oldkey, Type newkey); 34 // 将h1, h2合并成一个堆,并返回合并后的堆 35 FibHeap* fib_heap_union(FibHeap *h1, FibHeap *h2); 36 // 在斐波那契堆heap中是否存在键值为key的节点;存在返回1,否则返回0。 37 int fib_heap_contains(FibHeap *heap, Type key); 38 // 获取最小节点对应的值(保存在pkey中);成功返回1,失败返回0。 39 int fib_heap_get_min(FibHeap *heap, Type *pkey); 40 // 销毁斐波那契堆 41 void fib_heap_destroy(FibHeap *heap); 42 // 打印"斐波那契堆" 43 void fib_print(FibHeap *heap); 44 45 #endif
斐波那契堆的实现文件(fibonacci_heap.c)

1 /** 2 * C语言实现的斐波那契堆 3 * 4 * @author skywang 5 * @date 2014/04/05 6 */ 7 8 #include <stdio.h> 9 #include <stdlib.h> 10 #include <math.h> 11 #include <float.h> 12 #include "fibonacci_heap.h" 13 14 #if 0 15 #define LOG2(x) ({ \ 16 unsigned int _i = 0; \ 17 __asm__("bsr %1, %0" : "=r" (_i) : "r" ((x))); \ 18 _i; }) 19 #else // 注意:通过gcc编译时,要添加 -lm 选项。 20 #define LOG2(x) ((log((double)(x))) / (log(2.0))) 21 #endif 22 23 24 static FibNode *fib_heap_search(FibHeap *heap, Type key); 25 26 /* 27 * 将node从双链表移除 28 */ 29 static void fib_node_remove(FibNode *node) 30 { 31 node->left->right = node->right; 32 node->right->left = node->left; 33 } 34 35 /* 36 * 将"单个节点node"加入"链表root"之前 37 * a …… root 38 * a …… node …… root 39 * 40 * 注意: 此处node是单个节点,而root是双向链表 41 */ 42 static void fib_node_add(FibNode *node, FibNode *root) 43 { 44 node->left = root->left; 45 root->left->right = node; 46 node->right = root; 47 root->left = node; 48 } 49 50 /* 51 * 将双向链表b链接到双向链表a的后面 52 * 53 * 注意: 此处a和b都是双向链表 54 */ 55 static void fib_node_cat(FibNode *a, FibNode *b) 56 { 57 FibNode *tmp; 58 59 tmp = a->right; 60 a->right = b->right; 61 b->right->left = a; 62 b->right = tmp; 63 tmp->left = b; 64 } 65 66 67 /* 68 * 创建斐波那契堆 69 */ 70 FibHeap* fib_heap_make() 71 { 72 FibHeap* heap; 73 74 heap = (FibHeap *) malloc(sizeof(FibHeap)); 75 if (heap == NULL) 76 { 77 printf("Error: make FibHeap failed\n"); 78 return NULL; 79 } 80 81 heap->keyNum = 0; 82 heap->maxDegree = 0; 83 heap->min = NULL; 84 heap->cons = NULL; 85 86 return heap; 87 } 88 89 /* 90 * 创建斐波那契堆的节点 91 */ 92 static FibNode* fib_node_make(Type key) 93 { 94 FibNode * node; 95 96 node = (FibNode *) malloc(sizeof(FibNode)); 97 if (node == NULL) 98 { 99 printf("Error: make Node failed\n"); 100 return NULL; 101 } 102 node->key = key; 103 node->degree = 0; 104 node->left = node; 105 node->right = node; 106 node->parent = NULL; 107 node->child = NULL; 108 109 return node; 110 } 111 112 /* 113 * 将节点node插入到斐波那契堆heap中 114 */ 115 static void fib_heap_insert_node(FibHeap *heap, FibNode *node) 116 { 117 if (heap->keyNum == 0) 118 heap->min = node; 119 else 120 { 121 fib_node_add(node, heap->min); 122 if (node->key < heap->min->key) 123 heap->min = node; 124 } 125 heap->keyNum++; 126 } 127 128 /* 129 * 新建键值为key的节点,并将其插入到斐波那契堆中 130 */ 131 void fib_heap_insert_key(FibHeap *heap, Type key) 132 { 133 FibNode *node; 134 135 if (heap==NULL) 136 return ; 137 138 node = fib_node_make(key); 139 if (node == NULL) 140 return ; 141 142 fib_heap_insert_node(heap, node); 143 } 144 145 /* 146 * 将h1, h2合并成一个堆,并返回合并后的堆 147 */ 148 FibHeap* fib_heap_union(FibHeap *h1, FibHeap *h2) 149 { 150 FibHeap *tmp; 151 152 if (h1==NULL) 153 return h2; 154 if (h2==NULL) 155 return h1; 156 157 // 以h1为"母",将h2附加到h1上;下面是保证h1的度数大,尽可能的少操作。 158 if(h2->maxDegree > h1->maxDegree) 159 { 160 tmp = h1; 161 h1 = h2; 162 h2 = tmp; 163 } 164 165 if((h1->min) == NULL) // h1无"最小节点" 166 { 167 h1->min = h2->min; 168 h1->keyNum = h2->keyNum; 169 free(h2->cons); 170 free(h2); 171 } 172 else if((h2->min) == NULL) // h1有"最小节点" && h2无"最小节点" 173 { 174 free(h2->cons); 175 free(h2); 176 } // h1有"最小节点" && h2有"最小节点" 177 else 178 { 179 // 将"h2中根链表"添加到"h1"中 180 fib_node_cat(h1->min, h2->min); 181 if (h1->min->key > h2->min->key) 182 h1->min = h2->min; 183 h1->keyNum += h2->keyNum; 184 free(h2->cons); 185 free(h2); 186 } 187 188 return h1; 189 } 190 191 /* 192 * 将"堆的最小结点"从根链表中移除, 193 * 这意味着"将最小节点所属的树"从堆中移除! 194 */ 195 static FibNode *fib_heap_remove_min(FibHeap *heap) 196 { 197 FibNode *min = heap->min; 198 199 if (heap->min == min->right) 200 heap->min = NULL; 201 else 202 { 203 fib_node_remove(min); 204 heap->min = min->right; 205 } 206 min->left = min->right = min; 207 208 return min; 209 } 210 211 /* 212 * 将node链接到root根结点 213 */ 214 static void fib_heap_link(FibHeap * heap, FibNode * node, FibNode *root) 215 { 216 // 将node从双链表中移除 217 fib_node_remove(node); 218 // 将node设为root的孩子 219 if (root->child == NULL) 220 root->child = node; 221 else 222 fib_node_add(node, root->child); 223 224 node->parent = root; 225 root->degree++; 226 node->marked = 0; 227 } 228 229 /* 230 * 创建fib_heap_consolidate所需空间 231 */ 232 static void fib_heap_cons_make(FibHeap * heap) 233 { 234 int old = heap->maxDegree; 235 236 // 计算log2(x),"+1"意味着向上取整! 237 // ex. log2(13) = 3,向上取整为3+1=4。 238 heap->maxDegree = LOG2(heap->keyNum) + 1; 239 240 // 如果原本空间不够,则再次分配内存 241 if (old >= heap->maxDegree) 242 return ; 243 244 // 因为度为heap->maxDegree可能被合并,所以要maxDegree+1 245 heap->cons = (FibNode **)realloc(heap->cons, 246 sizeof(FibHeap *) * (heap->maxDegree + 1)); 247 } 248 249 /* 250 * 合并斐波那契堆的根链表中左右相同度数的树 251 */ 252 static void fib_heap_consolidate(FibHeap *heap) 253 { 254 int i, d, D; 255 FibNode *x, *y, *tmp; 256 257 fib_heap_cons_make(heap);//开辟哈希所用空间 258 D = heap->maxDegree + 1; 259 260 for (i = 0; i < D; i++) 261 heap->cons[i] = NULL; 262 263 // 合并相同度的根节点,使每个度数的树唯一 264 while (heap->min != NULL) 265 { 266 x = fib_heap_remove_min(heap); // 取出堆中的最小树(最小节点所在的树) 267 d = x->degree; // 获取最小树的度数 268 // heap->cons[d] != NULL,意味着有两棵树(x和y)的"度数"相同。 269 while (heap->cons[d] != NULL) 270 { 271 y = heap->cons[d]; // y是"与x的度数相同的树" 272 if (x->key > y->key) // 保证x的键值比y小 273 { 274 tmp = x; 275 x = y; 276 y = tmp; 277 278 } 279 fib_heap_link(heap, y, x); // 将y链接到x中 280 heap->cons[d] = NULL; 281 d++; 282 } 283 heap->cons[d] = x; 284 } 285 heap->min = NULL; 286 287 // 将heap->cons中的结点重新加到根表中 288 for (i=0; i<D; i++) 289 { 290 if (heap->cons[i] != NULL) 291 { 292 if (heap->min == NULL) 293 heap->min = heap->cons[i]; 294 else 295 { 296 fib_node_add(heap->cons[i], heap->min); 297 if ((heap->cons[i])->key < heap->min->key) 298 heap->min = heap->cons[i]; 299 } 300 } 301 } 302 } 303 304 /* 305 * 移除最小节点,并返回移除节点后的斐波那契堆 306 */ 307 FibNode* _fib_heap_extract_min(FibHeap *heap) 308 { 309 if (heap==NULL || heap->min==NULL) 310 return NULL; 311 312 FibNode *child = NULL; 313 FibNode *min = heap->min; 314 // 将min每一个儿子(儿子和儿子的兄弟)都添加到"斐波那契堆的根链表"中 315 while (min->child != NULL) 316 { 317 child = min->child; 318 fib_node_remove(child); 319 if (child->right == child) 320 min->child = NULL; 321 else 322 min->child = child->right; 323 324 fib_node_add(child, heap->min); 325 child->parent = NULL; 326 } 327 328 // 将min从根链表中移除 329 fib_node_remove(min); 330 // 若min是堆中唯一节点,则设置堆的最小节点为NULL; 331 // 否则,设置堆的最小节点为一个非空节点(min->right),然后再进行调节。 332 if (min->right == min) 333 heap->min = NULL; 334 else 335 { 336 heap->min = min->right; 337 fib_heap_consolidate(heap); 338 } 339 heap->keyNum--; 340 341 return min; 342 } 343 344 void fib_heap_extract_min(FibHeap *heap) 345 { 346 FibNode *node; 347 348 if (heap==NULL || heap->min==NULL) 349 return ; 350 351 node = _fib_heap_extract_min(heap); 352 if (node!=NULL) 353 free(node); 354 } 355 356 /* 357 * 在斐波那契堆heap中是否存在键值为key的节点;存在返回1,否则返回0。 358 */ 359 int fib_heap_get_min(FibHeap *heap, Type *pkey) 360 { 361 if (heap==NULL || heap->min==NULL || pkey==NULL) 362 return 0; 363 364 *pkey = heap->min->key; 365 return 1; 366 } 367 368 /* 369 * 修改度数 370 */ 371 static void renew_degree(FibNode *parent, int degree) 372 { 373 parent->degree -= degree; 374 if (parent-> parent != NULL) 375 renew_degree(parent->parent, degree); 376 } 377 378 /* 379 * 将node从父节点parent的子链接中剥离出来, 380 * 并使node成为"堆的根链表"中的一员。 381 */ 382 static void fib_heap_cut(FibHeap *heap, FibNode *node, FibNode *parent) 383 { 384 fib_node_remove(node); 385 renew_degree(parent, node->degree); 386 // node没有兄弟 387 if (node == node->right) 388 parent->child = NULL; 389 else 390 parent->child = node->right; 391 392 node->parent = NULL; 393 node->left = node->right = node; 394 node->marked = 0; 395 // 将"node所在树"添加到"根链表"中 396 fib_node_add(node, heap->min); 397 } 398 399 /* 400 * 对节点node进行"级联剪切" 401 * 402 * 级联剪切:如果减小后的结点破坏了最小堆性质, 403 * 则把它切下来(即从所在双向链表中删除,并将 404 * 其插入到由最小树根节点形成的双向链表中), 405 * 然后再从"被切节点的父节点"到所在树根节点递归执行级联剪枝 406 */ 407 static void fib_heap_cascading_cut(FibHeap *heap, FibNode *node) 408 { 409 FibNode *parent = node->parent; 410 if (parent != NULL) 411 return ; 412 413 if (node->marked == 0) 414 node->marked = 1; 415 else 416 { 417 fib_heap_cut(heap, node, parent); 418 fib_heap_cascading_cut(heap, parent); 419 } 420 } 421 422 /* 423 * 将斐波那契堆heap中节点node的值减少为key 424 */ 425 static void fib_heap_decrease(FibHeap *heap, FibNode *node, Type key) 426 { 427 FibNode *parent; 428 429 if (heap==NULL || heap->min==NULL ||node==NULL) 430 return ; 431 432 if ( key>=node->key) 433 { 434 printf("decrease failed: the new key(%d) is no smaller than current key(%d)\n", key, node->key); 435 return ; 436 } 437 438 node->key = key; 439 parent = node->parent; 440 if (parent!=NULL && node->key < parent->key) 441 { 442 // 将node从父节点parent中剥离出来,并将node添加到根链表中 443 fib_heap_cut(heap, node, parent); 444 fib_heap_cascading_cut(heap, parent); 445 } 446 447 // 更新最小节点 448 if (node->key < heap->min->key) 449 heap->min = node; 450 } 451 452 /* 453 * 将斐波那契堆heap中节点node的值增加为key 454 */ 455 static void fib_heap_increase(FibHeap *heap, FibNode *node, Type key) 456 { 457 FibNode *child, *parent, *right; 458 459 if (heap==NULL || heap->min==NULL ||node==NULL) 460 return ; 461 462 if (key <= node->key) 463 { 464 printf("increase failed: the new key(%d) is no greater than current key(%d)\n", key, node->key); 465 return ; 466 } 467 468 // 将node每一个儿子(不包括孙子,重孙,...)都添加到"斐波那契堆的根链表"中 469 while (node->child != NULL) 470 { 471 child = node->child; 472 fib_node_remove(child); // 将child从node的子链表中删除 473 if (child->right == child) 474 node->child = NULL; 475 else 476 node->child = child->right; 477 478 fib_node_add(child, heap->min); // 将child添加到根链表中 479 child->parent = NULL; 480 } 481 node->degree = 0; 482 node->key = key; 483 484 // 如果node不在根链表中, 485 // 则将node从父节点parent的子链接中剥离出来, 486 // 并使node成为"堆的根链表"中的一员, 487 // 然后进行"级联剪切" 488 // 否则,则判断是否需要更新堆的最小节点 489 parent = node->parent; 490 if(parent != NULL) 491 { 492 fib_heap_cut(heap, node, parent); 493 fib_heap_cascading_cut(heap, parent); 494 } 495 else if(heap->min == node) 496 { 497 right = node->right; 498 while(right != node) 499 { 500 if(node->key > right->key) 501 heap->min = right; 502 right = right->right; 503 } 504 } 505 } 506 507 /* 508 * 更新二项堆heap的节点node的键值为key 509 */ 510 void _fib_heap_update(FibHeap *heap, FibNode *node, Type key) 511 { 512 if(key < node->key) 513 fib_heap_decrease(heap, node, key); 514 else if(key > node->key) 515 fib_heap_increase(heap, node, key); 516 else 517 printf("No need to update!!!\n"); 518 } 519 520 void fib_heap_update(FibHeap *heap, Type oldkey, Type newkey) 521 { 522 FibNode *node; 523 524 if (heap==NULL) 525 return ; 526 527 node = fib_heap_search(heap, oldkey); 528 if (node!=NULL) 529 _fib_heap_update(heap, node, newkey); 530 } 531 532 /* 533 * 在最小堆root中查找键值为key的节点 534 */ 535 static FibNode* fib_node_search(FibNode *root, Type key) 536 { 537 FibNode *t = root; // 临时节点 538 FibNode *p = NULL; // 要查找的节点 539 540 if (root==NULL) 541 return root; 542 543 do 544 { 545 if (t->key == key) 546 { 547 p = t; 548 break; 549 } 550 else 551 { 552 if ((p = fib_node_search(t->child, key)) != NULL) 553 break; 554 } 555 t = t->right; 556 } while (t != root); 557 558 return p; 559 } 560 561 /* 562 * 在斐波那契堆heap中查找键值为key的节点 563 */ 564 static FibNode *fib_heap_search(FibHeap *heap, Type key) 565 { 566 if (heap==NULL || heap->min==NULL) 567 return NULL; 568 569 return fib_node_search(heap->min, key); 570 } 571 572 /* 573 * 在斐波那契堆heap中是否存在键值为key的节点。 574 * 存在返回1,否则返回0。 575 */ 576 int fib_heap_contains(FibHeap *heap, Type key) 577 { 578 return fib_heap_search(heap,key)!=NULL ? 1: 0; 579 } 580 581 /* 582 * 删除结点node 583 */ 584 static void _fib_heap_delete(FibHeap *heap, FibNode *node) 585 { 586 Type min = heap->min->key; 587 fib_heap_decrease(heap, node, min-1); 588 _fib_heap_extract_min(heap); 589 free(node); 590 } 591 592 void fib_heap_delete(FibHeap *heap, Type key) 593 { 594 FibNode *node; 595 596 if (heap==NULL || heap->min==NULL) 597 return ; 598 599 node = fib_heap_search(heap, key); 600 if (node==NULL) 601 return ; 602 603 _fib_heap_delete(heap, node); 604 } 605 606 /* 607 * 销毁斐波那契堆 608 */ 609 static void fib_node_destroy(FibNode *node) 610 { 611 FibNode *start = node; 612 613 if(node == NULL) 614 return; 615 616 do { 617 fib_node_destroy(node->child); 618 // 销毁node,并将node指向下一个 619 node = node->right; 620 free(node->left); 621 } while(node != start); 622 } 623 624 void fib_heap_destroy(FibHeap *heap) 625 { 626 fib_node_destroy(heap->min); 627 free(heap->cons); 628 free(heap); 629 } 630 631 /* 632 * 打印"斐波那契堆" 633 * 634 * 参数说明: 635 * node -- 当前节点 636 * prev -- 当前节点的前一个节点(父节点or兄弟节点) 637 * direction -- 1,表示当前节点是一个左孩子; 638 * 2,表示当前节点是一个兄弟节点。 639 */ 640 static void _fib_print(FibNode *node, FibNode *prev, int direction) 641 { 642 FibonacciNode *start=node; 643 644 if (node==NULL) 645 return ; 646 do 647 { 648 if (direction == 1) 649 printf("%8d(%d) is %2d's child\n", node->key, node->degree, prev->key); 650 else 651 printf("%8d(%d) is %2d's next\n", node->key, node->degree, prev->key); 652 653 if (node->child != NULL) 654 _fib_print(node->child, node, 1); 655 656 // 兄弟节点 657 prev = node; 658 node = node->right; 659 direction = 2; 660 } while(node != start); 661 } 662 663 void fib_print(FibHeap *heap) 664 { 665 int i=0; 666 FibonacciNode *p; 667 668 if (heap==NULL || heap->min==NULL) 669 return ; 670 671 printf("== 斐波那契堆的详细信息: ==\n"); 672 p = heap->min; 673 do { 674 i++; 675 printf("%2d. %4d(%d) is root\n", i, p->key, p->degree); 676 677 _fib_print(p->child, p, 1); 678 p = p->right; 679 } while (p != heap->min); 680 printf("\n"); 681 }
斐波那契堆的测试程序(main.c)

1 /** 2 * C语言实现的斐波那契堆 3 * 4 * @author skywang 5 * @date 2014/04/06 6 */ 7 8 #include <stdio.h> 9 #include "fibonacci_heap.h" 10 11 #define DEBUG 0 12 13 #if DEBUG 14 #define log(x, ...) printf(x, __VA_ARGS__) 15 #else 16 #define log(x, ...) 17 #endif 18 19 #define LENGTH(a) ( (sizeof(a)) / (sizeof(a[0])) ) 20 21 // 共8个 22 int a[] = {12, 7, 25, 15, 28, 23 33, 41, 1}; 24 // 共14个 25 int b[] = {18, 35, 20, 42, 9, 26 31, 23, 6, 48, 11, 27 24, 52, 13, 2}; 28 29 // 验证"基本信息(斐波那契堆的结构)" 30 void test_basic() 31 { 32 int i; 33 int blen=LENGTH(b); 34 FibHeap *hb = fib_heap_make(); 35 36 // 斐波那契堆hb 37 printf("== 斐波那契堆(hb)中依次添加: "); 38 for(i=0; i<blen; i++) 39 { 40 printf("%d ", b[i]); 41 fib_heap_insert_key(hb, b[i]); 42 } 43 printf("\n"); 44 printf("== 斐波那契堆(hb)删除最小节点\n"); 45 fib_heap_extract_min(hb); 46 fib_print(hb); 47 48 fib_heap_destroy(hb); 49 } 50 51 // 验证"插入操作" 52 void test_insert() 53 { 54 int i; 55 int alen=LENGTH(a); 56 FibHeap *ha = fib_heap_make(); 57 58 // 斐波那契堆ha 59 printf("== 斐波那契堆(ha)中依次添加: "); 60 61 for(i=0; i<alen; i++) 62 { 63 printf("%d ", a[i]); 64 fib_heap_insert_key(ha, a[i]); 65 } 66 printf("\n"); 67 printf("== 斐波那契堆(ha)删除最小节点\n"); 68 fib_heap_extract_min(ha); 69 fib_print(ha); 70 71 // 插入50 72 printf("== 插入50\n"); 73 fib_heap_insert_key(ha, 50); 74 fib_print(ha); 75 76 fib_heap_destroy(ha); 77 } 78 79 // 验证"合并操作" 80 void test_union() 81 { 82 int i; 83 int alen=LENGTH(a); 84 int blen=LENGTH(b); 85 FibHeap *ha = fib_heap_make(); 86 FibHeap *hb = fib_heap_make(); 87 88 // 斐波那契堆ha 89 printf("== 斐波那契堆(ha)中依次添加: "); 90 91 for(i=0; i<alen; i++) 92 { 93 printf("%d ", a[i]); 94 fib_heap_insert_key(ha, a[i]); 95 } 96 printf("\n"); 97 printf("== 斐波那契堆(ha)删除最小节点\n"); 98 fib_heap_extract_min(ha); 99 fib_print(ha); 100 101 // 斐波那契堆hb 102 printf("== 斐波那契堆(hb)中依次添加: "); 103 for(i=0; i<blen; i++) 104 { 105 printf("%d ", b[i]); 106 fib_heap_insert_key(hb, b[i]); 107 } 108 printf("\n"); 109 printf("== 斐波那契堆(hb)删除最小节点\n"); 110 fib_heap_extract_min(hb); 111 fib_print(hb); 112 113 // 将"斐波那契堆hb"合并到"斐波那契堆ha"中。 114 printf("== 合并ha和hb\n"); 115 ha = fib_heap_union(ha, hb); 116 fib_print(ha); 117 118 // 销毁堆 119 fib_heap_destroy(ha); 120 } 121 122 // 验证"删除最小节点" 123 void test_remove_min() 124 { 125 int i; 126 int alen=LENGTH(a); 127 int blen=LENGTH(b); 128 FibHeap *ha = fib_heap_make(); 129 FibHeap *hb = fib_heap_make(); 130 131 // 斐波那契堆ha 132 printf("== 斐波那契堆(ha)中依次添加: "); 133 134 for(i=0; i<alen; i++) 135 { 136 printf("%d ", a[i]); 137 fib_heap_insert_key(ha, a[i]); 138 } 139 printf("\n"); 140 printf("== 斐波那契堆(ha)删除最小节点\n"); 141 fib_heap_extract_min(ha); 142 //fib_print(ha); 143 144 // 斐波那契堆hb 145 printf("== 斐波那契堆(hb)中依次添加: "); 146 for(i=0; i<blen; i++) 147 { 148 printf("%d ", b[i]); 149 fib_heap_insert_key(hb, b[i]); 150 } 151 printf("\n"); 152 printf("== 斐波那契堆(hb)删除最小节点\n"); 153 fib_heap_extract_min(hb); 154 //fib_print(hb); 155 156 // 将"斐波那契堆hb"合并到"斐波那契堆ha"中。 157 printf("== 合并ha和hb\n"); 158 ha = fib_heap_union(ha, hb); 159 fib_print(ha); 160 161 printf("== 删除最小节点\n"); 162 fib_heap_extract_min(ha); 163 fib_print(ha); 164 165 // 销毁堆 166 fib_heap_destroy(ha); 167 } 168 169 // 验证"减小节点" 170 void test_decrease() 171 { 172 int i; 173 int blen=LENGTH(b); 174 FibHeap *hb = fib_heap_make(); 175 176 // 斐波那契堆hb 177 printf("== 斐波那契堆(hb)中依次添加: "); 178 for(i=0; i<blen; i++) 179 { 180 printf("%d ", b[i]); 181 fib_heap_insert_key(hb, b[i]); 182 } 183 printf("\n"); 184 printf("== 斐波那契堆(hb)删除最小节点\n"); 185 fib_heap_extract_min(hb); 186 fib_print(hb); 187 188 printf("== 将20减小为2\n"); 189 fib_heap_update(hb, 20, 2); 190 fib_print(hb); 191 192 fib_heap_destroy(hb); 193 } 194 195 // 验证"增大节点" 196 void test_increase() 197 { 198 int i; 199 int blen=LENGTH(b); 200 FibHeap *hb = fib_heap_make(); 201 202 // 斐波那契堆hb 203 printf("== 斐波那契堆(hb)中依次添加: "); 204 for(i=0; i<blen; i++) 205 { 206 printf("%d ", b[i]); 207 fib_heap_insert_key(hb, b[i]); 208 } 209 printf("\n"); 210 printf("== 斐波那契堆(hb)删除最小节点\n"); 211 fib_heap_extract_min(hb); 212 fib_print(hb); 213 214 fib_heap_update(hb, 20, 60); 215 printf("== 将20增加为60\n"); 216 fib_print(hb); 217 218 fib_heap_destroy(hb); 219 } 220 221 // 验证"删除节点" 222 void test_delete() 223 { 224 int i; 225 int blen=LENGTH(b); 226 FibHeap *hb = fib_heap_make(); 227 228 // 斐波那契堆hb 229 printf("== 斐波那契堆(hb)中依次添加: "); 230 for(i=0; i<blen; i++) 231 { 232 printf("%d ", b[i]); 233 fib_heap_insert_key(hb, b[i]); 234 } 235 printf("\n"); 236 printf("== 斐波那契堆(hb)删除最小节点\n"); 237 fib_heap_extract_min(hb); 238 fib_print(hb); 239 240 fib_heap_delete(hb, 20); 241 printf("== 删除节点20\n"); 242 fib_print(hb); 243 244 fib_heap_destroy(hb); 245 } 246 247 void main() 248 { 249 // 验证"基本信息(斐波那契堆的结构)" 250 test_basic(); 251 // 验证"插入操作" 252 //test_insert(); 253 // 验证"合并操作" 254 //test_union(); 255 // 验证"删除最小节点" 256 //test_remove_min(); 257 // 验证"减小节点" 258 //test_decrease(); 259 // 验证"增大节点" 260 //test_increase(); 261 // 验证"删除节点" 262 //test_delete(); 263 }
斐波那契堆的C测试程序
斐波那契堆的测试程序包括了"插入"、"合并"、"增大"、"减小"、"删除"、"基本信息"等几种功能的测试代码。默认是运行的"基本信息(验证斐波那契堆的结构)"测试代码,你可以根据自己的需要来对相应的功能进行验证!
注意:C语言版的斐波那契堆的LOG2宏定义中使用了math.h,记得引入math库。例如,若你是在Linux下通过gcc编译,记得添加-lm参数(gcc *.c -lm)。
下面是基本信息测试代码的运行结果:
== 斐波那契堆(hb)中依次添加: 18 35 20 42 9 31 23 6 48 11 24 52 13 2 == 斐波那契堆(hb)删除最小节点 == 斐波那契堆的详细信息: ==1. 6(3) is root9(0) is 6's child18(1) is 9's next35(0) is 18's child20(2) is 18's next42(0) is 20's child23(1) is 42's next31(0) is 23's child2. 11(2) is root48(0) is 11's child24(1) is 48's next52(0) is 24's child3. 13(0) is root