斐波那契堆(一)之 圖文解析 和 C語言的實現

 

概要

本章介紹斐波那契堆。和以往同樣,本文會先對斐波那契堆的理論知識進行簡單介紹,而後給出C語言的實現。後續再分別給出C++和Java版本的實現;實現的語言雖不一樣,可是原理一模一樣,選擇其中之一進行了解便可。若文章有錯誤或不足的地方,請不吝指出! html

目錄
1. 斐波那契堆的介紹
2. 斐波那契堆的基本操做
3. 斐波那契堆的C實現(完整源碼)
4. 斐波那契堆的C測試程序node

轉載請註明出處:http://www.cnblogs.com/skywang12345/p/3659060.html算法


更多內容:數據結構與算法系列 目錄數據結構

(01) 斐波那契堆(一)之 圖文解析 和 C語言的實現 
(02) 斐波那契堆(二)之 C++的實現 
(03) 斐波那契堆(三)之 Java的實現
ide

 

斐波那契堆的介紹

斐波那契堆(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在刪除節點時來暫時保存堆數據的臨時空間。spa

 

下面看看斐波那契堆的內存結構圖。3d

從圖中能夠看出,斐波那契堆是由一組最小堆組成,這些最小堆的根節點組成了雙向鏈表(後文稱爲"根鏈表");斐波那契堆中的最小節點就是"根鏈表中的最小節點"!

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;
        else
            min->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 }
View Code

 

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 }
View Code

 

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;
        else
            node->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
View Code

斐波那契堆的實現文件(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 }
View Code

斐波那契堆的測試程序(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 }
View Code

 

斐波那契堆的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 root
       9(0) is  6's child
      18(1) is  9's next
      35(0) is 18's child
      20(2) is 18's next
      42(0) is 20's child
      23(1) is 42's next
      31(0) is 23's child
 2.   11(2) is root
      48(0) is 11's child
      24(1) is 48's next
      52(0) is 24's child
 3.   13(0) is root
相關文章
相關標籤/搜索