斐波那契堆(Fibonacci Heap)
来源:互联网 发布:京东众包软件打不开 编辑:程序博客网 时间:2024/05/21 15:31
也许我们每个人都知道斐波那契数列(Fibonacci sequence)。即这样一个数列:1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144...,如果我们用伪代码比表示:
int FibonacciSequence(int n){ if (n == 1 || n == 2) { return 1; } return FibonacciSequence(n - 1) + FibonacciSequence(n - 2);}接下来我们会知道为什么叫斐波那契堆,本文不涉及任何的分析(这里平摊分析(Amortized analysis))。
1. 概述
一个斐波那契堆是一系列具有最小堆序的有根树的集合。(也就是每棵树都遵循最小堆性质)[1]
2. 斐波那契堆的结构
[2]
从上面我们可能发现,我们堆斐波那契堆的描述,并不完全符合我们的定义(那时因为定义是针对在一次extractMin(删除最小值)操作后,使之符合我们的定义)。但从上面的结构图我们很容易看出其详细的结构,以及指针域。兄弟节点之间用双向链表进行链接,父节点指向其中一个孩子节点,而所有的子节点都会指向其父亲节点。有些节点还被标记了(后面我们会知道,只有当父节点失去孩子节点时,会被标记)。上述还有一个隐含的关键字那就是每个节点的度(即拥有孩子节点的个数)
3. 基本数据结构
typedef int ElemType;// the structure of each node of this heaptypedef struct FiboNode{ // the key_value ElemType data; struct FiboNode *p; struct FiboNode *child; struct FiboNode *left; struct FiboNode *right; // the number of its children int degree; // indicates whether the node lost a child int marked;} FiboNode;
// generate a nodeFiboNode * generateFiboNode(ElemType data) { FiboNode * fn = NULL; fn = (FiboNode *) malloc(sizeof(FiboNode)); if (fn != NULL) { fn->p = NULL; fn->child = NULL; fn->left = NULL; fn->right = NULL; fn->data = data; fn->marked = FALSE; } else { printf("memory allocation of FiboNode failed"); } return fn;}
// represent the fibonacci heaptypedef struct FibonacciHeap{ // pointer pointing to min key in the heap FiboNode *min; // the number of nodes the heap containing int n;} *FibonacciHeap;
// create Fibonacci Heap, and return itFibonacciHeap makeFiboHeap() { FibonacciHeap h = NULL; h = (struct FibonacciHeap *) malloc(sizeof(struct FibonacciHeap)); if (h == NULL) { printf("memory allocation of FibonacciHeap failed"); } h->min = NULL; h->n = 0; return h;}
4. 插入
插入其实很简单,就是把待插入的元素插入根列表(root list)中去,即上图[2]中min[H]指向的那一行。
// union two heapFibonacciHeap heapUnion(FibonacciHeap *h1, FibonacciHeap *h2) { // using a new heap FibonacciHeap h = makeFiboHeap(); if (h != NULL) { // concatenate the root list of h with h1 and h2 if ((*h1)->min == NULL) { h->min = (*h2)->min; } else if ((*h2)->min == NULL) { h->min = (*h1)->min; } else { FiboNode *min_h1 = (*h1)->min; FiboNode *min_right_h1 = min_h1->right; FiboNode *min_h2 = (*h2)->min; FiboNode *min_right_h2 = min_h2->right; min_h1->right = min_right_h2; min_right_h2->left = min_h1; min_h2->right = min_right_h1; min_right_h1->left = min_h2; if ((*h1)->min->data > (*h2)->min->data) { h->min = (*h2)->min; } else { h->min = (*h1)->min; } } // release the free memory free(*h1); *h1 = NULL; free(*h2); *h2 = NULL; // update the n h->n = (*h1)->n + (*h2)->n; } return h;}
5. 合并(Union)
这个操作只需要将两个斐波那契堆的根列表合并,并使得min[H]指向两者间的最小值节点。// union two heapFibonacciHeap heapUnion(FibonacciHeap *h1, FibonacciHeap *h2) { // using a new heap FibonacciHeap h = makeFiboHeap(); if (h != NULL) { // concatenate the root list of h with h1 and h2 if ((*h1)->min == NULL) { h->min = (*h2)->min; } else if ((*h2)->min == NULL) { h->min = (*h1)->min; } else { FiboNode *min_h1 = (*h1)->min; FiboNode *min_right_h1 = min_h1->right; FiboNode *min_h2 = (*h2)->min; FiboNode *min_right_h2 = min_h2->right; min_h1->right = min_right_h2; min_right_h2->left = min_h1; min_h2->right = min_right_h1; min_right_h1->left = min_h2; if ((*h1)->min->data > (*h2)->min->data) { h->min = (*h2)->min; } else { h->min = (*h1)->min; } } // release the free memory free(*h1); *h1 = NULL; free(*h2); *h2 = NULL; // update the n h->n = (*h1)->n + (*h2)->n; } return h;}
6. 抽取最小节点
从上面我们很容易知道如何操作的。
1)具体步骤如下:
step1. 让最小节点的的所有子节点添加到根列表中去。
step2. 从根列表中删除最小节点。
step3. 创建一个log(n) + 1 的节点指针数组A。
step4. 对根列表进行合并,使得其满足我们堆斐波那契堆的定义(即根列表中的节点的度数具有唯一性)
// extract minimum node in this heapFiboNode * extractMinimum(FibonacciHeap h) { // the minimum node FiboNode * z = h->min; if (z != NULL) { FiboNode * firstChid = z->child; // add the children of minimum node to the root list. if (firstChid != NULL) { FiboNode * sibling = firstChid->right; // min_right point the right node of minimum node FiboNode * min_right = z->right; // add the first child to the root list z->right = firstChid; firstChid->left = z; min_right->left = firstChid; firstChid->right = min_right; firstChid->p = NULL; min_right = firstChid; while (firstChid != sibling) { // record the right sibling of sibling FiboNode *sibling_right = sibling->right; z->right = sibling; sibling->left = z; sibling->right = min_right; min_right->left = sibling; min_right = sibling; sibling = sibling_right; // update the p sibling->p = NULL; } } // remove z from the root list z->left->right = z->right; z->right->left = z->left; // the root list has only one node if (z == z->right) { h->min = NULL; // the children of z shoud be the root list of the heap // and find the minimum in this heap if (z->child != NULL) { FiboNode *child = z->child; h->min = child; FiboNode *sibling = child->right; while (child != sibling) { if (h->min->data > sibling->data) { h->min = sibling; } sibling = sibling->right; } } } else { h->min = z->right; consolidate(h); } h->n -= 1; } return z;}
2)对根列表进行合并
1. link操作
把节点y链接到x:并把y从根列表中删除,此时x会成为y的父亲,y会成为x的孩子,同时x节点的度数(degree)会增加1.
// make y a child of xvoid heapLink(FibonacciHeap h, FiboNode *y, FiboNode *x) { // remove y from the root list of h y->left->right = y->right; y->right->left = y->left; // make y a child of x, incrementing x.degree FiboNode * child = x->child; if (child == NULL) { x->child = y; y->left = y; y->right = y; } else { y->right = child->right; child->right->left = y; y->left = child; child->right = y; } y->p = x; x->degree += 1; y->marked = FALSE;}
2. 合并
创建一个辅助数组A[0, 1..., D(H.n)]来记录根节点对应的度数的轨迹。如果A[i] = y, 则degree[y] = i.通过遍历根列表,若对应度数的数组A[i]为NILL,表示还没有记录,则使得A[i] = y, 否则存在两个度数相同的度数的根列表节点,则我们需要对其进行link操作,使之度数增加1,并设置A[i] = NILL, 然后再探查A[i + 1]是否为NIll, 为NILL则记录,否则再进行link操作,这样循环往复。
void consolidate(FibonacciHeap h) { int dn = (int)(log(h->n) / log(2)) + 1; FiboNode * A[dn]; int i; for (i = 0; i < dn; ++i) { A[i] = NULL; } // the first node we will consolidate FiboNode *w = h->min; // the final node in this heap we will consolidate FiboNode *f = w->left; FiboNode *x = NULL; FiboNode *y = NULL; int d; // temp FiboNode *t = NULL; while (w != f) { d = w->degree; x = w; w = w->right; while (A[d] != NULL) { // another node with the same degree as x y = A[d]; if(x->data > y->data) { t = x; x = y; y = t; } heapLink(h, y, x); A[d] = NULL; d += 1; } A[d] = x; } // the last node to consolidate (f == w) d = w->degree; x = w; while (A[d] != NULL) { // another node with the same degree as x y = A[d]; if(x->data > y->data) { t = x; x = y; y = t; } heapLink(h, y, x); A[d] = NULL; d += 1; } A[d] = x; int min_key = 100000; h->min = NULL; // to get min in this heap for (i = 0; i < dn; ++i) { if(A[i] != NULL && A[i]->data < min_key) { h->min = A[i]; min_key = A[i]->data; } }}
7. 降低节点的值
直接上代码这样,接下来分析才比较容易。// decrease the data of given node to the keyvoid heapDecreaseKey(FibonacciHeap h, FiboNode *x, ElemType key) { if (key >= x->data) { printf("new key is's smaller than original value"); return; } x->data = key; FiboNode *y = x->p; // if x->data >= y->data, do nothing // else we need cut if (y != NULL && x->data < y->data) { cut(h, x, y); cascadingCut(h, y); } // get min node of this heap if (x->data < h->min->data) { h->min = x; }}
// if we cut node x from y, it indicates root list of// h not nullvoid cut(FibonacciHeap h, FiboNode *x, FiboNode *y) { // remove x from child list of y, decrementing degree[y] if(y->degree == 1) { y->child = NULL; } else { x->left->right = x->right; x->right->left = x->left; // update child[y] y->child = x->right; } // add x to the root list of h x->left = h->min; x->right = h->min->right; h->min->right = x; x->right->left = x; // updating p[x], marked[x] and decrementing degree[y] x->p = NULL; x->marked = FALSE; y->degree -= 1;}
void cascadingCut(FibonacciHeap h, FiboNode *y) { FiboNode *z = y->p; if (z != NULL) { if (y->marked == FALSE) { y->marked = TRUE; } else { cut(h, y, z); cascadingCut(h, z); } }}通过上面的发现我们知道:
1)当我们降低节点x的值时,值一定要小于当前节点x的值
2)若节点x的值比其父亲要小,则已经满足不了最小堆性质
3)当不满足最小堆性质的时候,我们要将其与其父亲y切断,并将x添加到根列表中去
4)若x的父亲在失去第一个孩子的时候则要Mark = true,当y失去第2个孩子,则y要和y的父亲z切断,接着这样不断上升操作。
8. 删除
删除的操作比较简单,就两步:1)将节点的值降低到无穷小
2)进行extractMinimum() 操作
void heapDelete(FibonacciHeap h, FiboNode *x) {
heapDecreaseKey(h, x, -10000);
extractMinimum(h);
}
参考:
[1] Thomas H.Cormen、Charles E.Leiserson《算法导论》第三版 第十九章 “斐波那契对” p291[2] Thomas H.Cormen、Charles E.Leiserson《算法导论》第三版 第十九章 “斐波那契对” p291
[2] Thomas H.Cormen、Charles E.Leiserson《算法导论》第三版 第十九章 “斐波那契对” p295
感谢 Thomas H.Cormen、Charles E.Leiserson等人
若有参考遗漏的请抱歉,也请您留言
若其中有误的请多多留言不甚感激
完整源码,请点击
注:转载请注明
- 斐波那契堆(Fibonacci heap)
- 斐波那契堆(fibonacci heap)基础
- 斐波那契堆(Fibonacci Heap)
- 斐波那契堆(Fibonacci heap)原理详解
- 又碰到一个新的概念:斐波那契堆(Fibonacci heap)
- 斐波那契堆(Fibonacci heap)原理详解(附java代码实现)
- 斐波那契堆(Fibonacci heap)原理详解(附java代码实现)
- 斐波那契堆(Fibonacci heaps)
- 斐波那契堆(Fibonacci heaps)
- 结构之美——优先队列三大结构(二)——斐波那契堆(Fibonacci Heap)
- 斐波那契(Fibonacci)数列
- 斐波那契数列(Fibonacci)
- 斐波那契(Fibonacci)数列
- 斐波那契数列(Fibonacci)
- 斐波那契(Fibonacci)数列
- 斐波那契(Fibonacci)数列
- 斐波那契数列(Fibonacci)
- 斐波那契数列(Fibonacci)
- ROSArduinoBridge中PID 在 ros端 与arduino端的工作机制
- HDU 1029 Ignatius and the Princess IV
- C. Andryusha and Colored Balloons
- 装配Bean
- Android -- RecyclerView实现顶部吸附效果
- 斐波那契堆(Fibonacci Heap)
- Swift Web 开发之 Vapor
- AppleScript: match and delete files in folder
- CodeForces782B The Meeting Place Cannot Be Changed 三分查找
- python 发送163邮件554 DT:SPM
- height:auto和height:100%的区别
- 阿里云部署的时候常用的Linux步骤
- Swift Web 开发之 Vapor
- 算法5:求两个已排序数组的交集和并集