现在的位置: 首页 > 综合 > 正文

优先队列三大利器——二项堆、斐波那契堆、Pairing 堆

2018年04月20日 ⁄ 综合 ⁄ 共 39566字 ⁄ 字号 评论关闭

原文地址:http://dsqiu.iteye.com/blog/1714961

       这篇博文主要介绍二项堆、斐波那契堆、Pairing 堆,都知道只要这三个结构应用实现优先队列的,讲解还是比较详细,参考了很多资料,尤其是Pairing堆很少有讲解,但还是力所能及的写了,虽然可能正确很难保证,其次就是斐波那契堆我对于减小一个关键字的操作的级联剪枝不是很理解(不知道为什么要这么做,难道是为了追求好的时间复杂度)还望高人指点,具体细节在下文会有很多介绍。

 

下面对这三者进行对比

其中(*)Amortized time
(**)With trivial modification to store an additional pointer to the minimum element
(***)Where n is the size of the larger heap

 

二项堆(Binomial Heap)

二项堆(Binomial Heap)是二项树(Binomial Tree)的集合(collection),所以势必要先简要介绍下二项树。关于堆(最小堆)的相关知识,作者已经在堆排序有介绍可以点击查看,这里就不多枚举了。

二项树(Binomial Tree)

二项树(Binomial Tree)是一组多分支树的序列,二项树的递归定义:

  • 二项树的第0棵树只有一个结点;
  • 二项树的第K棵树的子结点由二项树的前面k-1棵组成的。

从以上定义,不难得到下面的结论:

 

  • 二项树的第K棵树有 2k个结点,高度是k;
  • 深度为 d 的结点共有\tbinom n d个结点(从上图就可以看出结点是按二项分布的(1,3,3,1))

二项堆由一组二项树所构成,这里的二项树需要满足下列条件:

1)H中的每个二项树遵循最小堆的性质。

2)对于任意非负整数k,在H中至多有一棵二项树的根具有度数k。

 

对于性质2,任意高度最多有一棵二项树,这样就可以用二项树的集合唯一地表示任意大小的二项堆,比如13个结点的二项堆H,13的二进制表示为(1101),故H包含了最小堆有序二项树B3, B2和B0, 他们分别有8, 4, 2, 1个结点,即共有13个结点。如下图(另外:二项堆中各二项树的根被组织成一个链表,称之为根表)

二项树的ADT

 

C代码  收藏代码
  1. typedef struct BinHeapNode BinNode;  
  2. typedef struct BinHeapNode * BinHeap;  
  3. typedef struct BinHeapNode * Position;  
  4.    
  5. //结点ADT  
  6. struct BinHeapNode {  
  7.     int key;  
  8.     int degree;  
  9.     Position parent;  
  10.     Position leftChild;  
  11.     Position sibling;  
  12. };  

 

二项dui的操作

1)创建二项堆 

 

C代码  收藏代码
  1. BinHeap MakeBinHeap() {  
  2.     BinHeap heap = NULL;  
  3.     heap = (BinHeap) malloc(sizeof(BinNode));  
  4.     if (heap == NULL) {  
  5.         puts("Out of the Space");  
  6.         exit(1);  
  7.     }  
  8.     memset(newHeap, 0, sizeof(BinNode));  
  9.     return heap;  
  10. }  

 

2)寻找最小关键字

由于每一个二项树都满足最小堆的性质,所以每个二项树的最小关键字一定在根结点,故只需遍历比较根表的情况就可以。

C代码  收藏代码
  1. //返回最小根节点的指针  
  2. BinHeap BinHeapMin(BinHeap heap) {  
  3.     Position y = NULL, x = heap;  
  4.     int min = INT_MAX;  
  5.    
  6.     while (x != NULL) {  
  7.         if (x->key < min) {  
  8.             min = x->key;  
  9.             y = x;  
  10.         }  
  11.         x = x->sibling;  
  12.     }  
  13.     return y;  
  14. }  

3)合并两个二项堆

合并两个二项堆有三个函数:

 

BINOMIAL-LINK,连接操作,即将两棵根节点度数相同的二项树Bk-1连接成一棵Bk。伪代码:

BINOMIAL-HEAP-MERGE ,将H1和H2的根表合并成一个按度数的单调递增次序排列的链表。

BINOMIAL-HEAP-UNION,反复连接根节点的度数相同的各二项树。伪代码:

合并操作分为两个阶段:

第一阶段:执行BINOMIAL-HEAP-MERGE,将两个堆H1和H2的根表合并成一个链表H,它按度数排序成单调递增次序。MERGE的时间复杂度O(logn)。n为H1和H2的结点总数。(对于每一个度数值,可能有两个根与其对应,所以第二阶段要把这些相同的根连起来)。

第二阶段:将相等度数的根连接起来,直到每个度数至多有一个根时为止。执行过程中,合并的堆H的根表中至多出现三个根具有相同的度数。(MERGE后H中至多出现两个根具有相同的度数,但是将两个相同度数的根的二项树连接后,可能与后面的至多两棵二项树出现相同的度数的根,因此至多出现三个根具有相同的度数)

 

第二阶段根据当前遍历到的根表中的结点x,分四种情况考虑:

Case1:degree[x] != degree[sibling[x]]。此时,不需要做任何变化,将指针向根表后移动即可。(下图示a)

Case2:degree[x] == degree[sibling[x]] == degree[sibling[sibling[x]]]。此时,仍不做变化,将指针后移。(下图示b)

Case3 & Case4:degree[x] = degree[sibling[x]] != degree[sibling[sibling[x]]] (下图示c和d)

Case3:key[x] <= key[sibling[x]]。此时,将sibling[x]连接到x上。

Case4:key[x] > key[sibling[x]]。此时,将x连接到sibling[x]上。

复杂度:O(logn), 四个过程变化情况:

 

 

C代码  收藏代码
  1. //两个堆合并  
  2. BinHeap BinHeapUnion(BinHeap &H1, BinHeap &H2) {  
  3.     Position heap = NULL, pre_x = NULL, x = NULL, next_x = NULL;  
  4.     heap = BinHeapMerge(H1, H2);  
  5.     if (heap == NULL) {  
  6.         return heap;  
  7.     }  
  8.    
  9.     pre_x = NULL;  
  10.     x = heap;  
  11.     next_x = x->sibling;  
  12.    
  13.     while (next_x != NULL) {  
  14.         if ((x->degree != next_x->degree) ||//Cases 1 and 2  
  15.             ((next_x->sibling != NULL) && (next_x->degree == next_x->sibling->degree))) {  
  16.                 pre_x = x;  
  17.                 x = next_x;  
  18.         } else if (x->key <= next_x->key) {//Cases 3  
  19.             x->sibling = next_x->sibling;  
  20.             BinLink(next_x, x);  
  21.         } else {//Cases 4  
  22.             if (pre_x == NULL) {  
  23.                 heap = next_x;  
  24.             } else {  
  25.                 pre_x->sibling = next_x;  
  26.             }  
  27.             BinLink(x, next_x);  
  28.             x = next_x;  
  29.         }  
  30.         next_x = x->sibling;  
  31.     }  
  32.     return heap;  
  33. }  
  34.    
  35. //将H1, H2的根表合并成一个按度数的单调递增次序排列的链表  
  36. BinHeap BinHeapMerge(BinHeap &H1, BinHeap &H2) {  
  37.     //heap->堆的首地址,H3为指向新堆根结点  
  38.     BinHeap heap = NULL, firstHeap = NULL, secondHeap = NULL,  
  39.         pre_H3 = NULL, H3 = NULL;  
  40.    
  41.     if (H1 != NULL && H2 != NULL){  
  42.         firstHeap = H1;  
  43.         secondHeap = H2;  
  44.         //整个while,firstHeap, secondHeap, pre_H3, H3都在往后顺移  
  45.         while (firstHeap != NULL && secondHeap != NULL) {  
  46.             if (firstHeap->degree <= secondHeap->degree) {  
  47.                 H3 = firstHeap;  
  48.                 firstHeap = firstHeap->sibling;  
  49.             } else {  
  50.                 H3 = secondHeap;  
  51.                 secondHeap = secondHeap->sibling;  
  52.             }  
  53.    
  54.             if (pre_H3 == NULL) {  
  55.                 pre_H3 = H3;  
  56.                 heap = H3;  
  57.             } else {  
  58.                 pre_H3->sibling = H3;  
  59.                 pre_H3 = H3;  
  60.             }  
  61.             if (firstHeap != NULL) {  
  62.                 H3->sibling = firstHeap;  
  63.             } else {  
  64.                 H3->sibling = secondHeap;  
  65.             }  
  66.         }//while  
  67.     } else if (H1 != NULL) {  
  68.         heap = H1;  
  69.     } else {  
  70.         heap = H2;  
  71.     }  
  72.     H1 = H2 = NULL;  
  73.     return heap;  
  74. }  
  75.    
  76. //使H2成为H1的父节点  
  77. void BinLink(BinHeap &H1, BinHeap &H2) {  
  78.     H1->parent = H2;  
  79.     H1->sibling = H2->leftChild;  
  80.     H2->leftChild = H1;  
  81.     H2->degree++;  
  82. }  

4)插入一个结点

先创建只有该结点的二项堆,然后在与原来的二项堆合并。

C代码  收藏代码
  1. //用数组内的值建堆  
  2. BinHeap MakeBinHeapWithArray(int keys[], int n) {  
  3.     BinHeap heap = NULL, newHeap = NULL;  
  4.     for (int i = 0; i < n; i++) {  
  5.         newHeap = (BinHeap) malloc(sizeof(BinNode));  
  6.         if (newHeap == NULL) {  
  7.             puts("Out of the Space");  
  8.             exit(1);  
  9.         }  
  10.         memset(newHeap, 0, sizeof(BinNode));  
  11.         newHeap->key = keys[i];  
  12.         if (NULL == heap) {  
  13.             heap = newHeap;  
  14.         } else {  
  15.             heap = BinHeapUnion(heap, newHeap);  
  16.             newHeap = NULL;  
  17.         }  
  18.     }  
  19.     return heap;  
  20. }  

5)删除最小关键字的结点

从根表中找到最小关键字的结点,将以该结点为根的整棵二项树从堆取出,删除取出的二项树的根,将其剩下的子女倒序排列,组成了一个新的二项堆,再与之前的二项堆合并。  

 

Cpp代码  收藏代码
  1. //抽取有最小关键字的结点  
  2. BinHeap BinHeapExtractMin(BinHeap &heap) {  
  3.     BinHeap pre_y = NULL, y = NULL, x = heap;  
  4.     int min = INT_MAX;  
  5.     while (x != NULL) {  
  6.         if (x->key < min) {  
  7.             min = x->key;  
  8.             pre_y = y;  
  9.             y = x;  
  10.         }  
  11.         x = x->sibling;  
  12.     }  
  13.    
  14.     if (y == NULL) {  
  15.         return NULL;  
  16.     }  
  17.    
  18.     if (pre_y == NULL) {  
  19.         heap = heap->sibling;  
  20.     } else {  
  21.         pre_y->sibling = y->sibling;  
  22.     }  
  23.    
  24.     //将y的子结点指针reverse  
  25.     BinHeap H2 = NULL, p = NULL;  
  26.     x = y->leftChild;  
  27.     while (x != NULL) {  
  28.         p = x;  
  29.         x = x->sibling;  
  30.         p->sibling = H2;  
  31.         H2 = p;  
  32.         p->parent = NULL;  
  33.     }  
  34.    
  35.     heap = BinHeapUnion(heap, H2);  
  36.     return y;  
  37. }  

 

 6)减小关键字的值

减小关键字的值其实就是更新关键字的值,实现过程就是维护最小堆的过程。

 

C代码  收藏代码
  1. //减少关键字的值  
  2. void BinHeapDecreaseKey(BinHeap heap, BinHeap x, int key) {  
  3.     if(key > x->key) {  
  4.         puts("new key is greaer than current key");  
  5.         exit(1); //不为降键  
  6.     }  
  7.     x->key = key;  
  8.    
  9.     BinHeap z = NULL, y = NULL;  
  10.     y = x;  
  11.     z = x->parent;  
  12.     while(z != NULL && z->key > y->key) {  
  13.         swap(z->key, y->key);  
  14.         y = z;  
  15.         z = y->parent;  
  16.     }  
  17. }  

 

7)删除一个关键字

删除一个关键字转换为前面的过程——将该关键字修改为最小值(要维护最小堆的性质),然后变成删除最小关键字的过程。 

 

C代码  收藏代码
  1. //删除一个关键字  
  2. BinHeap BinHeapDelete(BinHeap &heap, int key) {  
  3.     BinHeap x = NULL;  
  4.     x = BinHeapFind(heap, key);  
  5.     if (x != NULL) {  
  6.         BinHeapDecreaseKey(heap, x, INT_MIN);  
  7.         return BinHeapExtractMin(heap);  
  8.     }  
  9.     return x;  
  10. }  
  11.    
  12. //找出一个关键字  
  13. BinHeap BinHeapFind(BinHeap &heap, int key) {  
  14.     Position p = NULL, x = NULL;  
  15.     p = heap;  
  16.     while (p != NULL) {  
  17.         if (p->key == key) {  
  18.             return p;  
  19.         } else {  
  20.             if((x =BinHeapFind(p->leftChild, key)) != NULL) {  
  21.                 return x;  
  22.             }  
  23.             p = p->sibling;  
  24.         }  
  25.     }  
  26.     return NULL;  
  27. }  

 

 

二项树的完整实现

 

C代码  收藏代码
  1. #include<iostream>  
  2. #include<cstdio>  
  3. #include<cstring>  
  4. #include<cstdlib>  
  5. #include<climits>  
  6. using namespace std;  
  7.    
  8. typedef struct BinHeapNode BinNode;  
  9. typedef struct BinHeapNode * BinHeap;  
  10. typedef struct BinHeapNode * Position;  
  11.    
  12. //结点ADT  
  13. struct BinHeapNode {  
  14.     int key;  
  15.     int degree;  
  16.     Position parent;  
  17.     Position leftChild;  
  18.     Position sibling;  
  19. };  
  20.    
  21. //用数组内的值建堆  
  22. BinHeap MakeBinHeapWithArray(int keys[], int n);  
  23.    
  24. //两个堆合并  
  25. BinHeap BinHeapUnion(BinHeap &H1, BinHeap &H2);  
  26.    
  27. //将H1, H2的根表合并成一个按度数的单调递增次序排列的链表  
  28. BinHeap BinHeapMerge(BinHeap &H1, BinHeap &H2);  
  29.    
  30. //使H2成为H1的父节点  
  31. void BinLink(BinHeap &H1, BinHeap &H2);  
  32.    
  33. //返回最小根节点的指针  
  34. BinHeap BinHeapMin(BinHeap heap);  
  35.    
  36. //减少关键字的值  
  37. void BinHeapDecreaseKey(BinHeap heap, BinHeap x, int key);  
  38.    
  39. //删除一个关键字  
  40. BinHeap BinHeapDelete(BinHeap &heap, int key);  
  41.    
  42. //找出一个关键字  
  43. BinHeap BinHeapFind(BinHeap &heap, int key);  
  44.    
  45. //打印输出堆结构  
  46. void PrintBinHeap(BinHeap heap);  
  47.    
  48. //销毁堆  
  49. void DestroyBinHeap(BinHeap &heap);  
  50.    
  51. //用数组内的值建堆  
  52. BinHeap MakeBinHeapWithArray(int keys[], int n) {  
  53.     BinHeap heap = NULL, newHeap = NULL;  
  54.     for (int i = 0; i < n; i++) {  
  55.         newHeap = (BinHeap) malloc(sizeof(BinNode));  
  56.         if (newHeap == NULL) {  
  57.             puts("Out of the Space");  
  58.             exit(1);  
  59.         }  
  60.         memset(newHeap, 0, sizeof(BinNode));  
  61.         newHeap->key = keys[i];  
  62.         if (NULL == heap) {  
  63.             heap = newHeap;  
  64.         } else {  
  65.             heap = BinHeapUnion(heap, newHeap);  
  66.             newHeap = NULL;  
  67.         }  
  68.     }  
  69.     return heap;  
  70. }  
  71.    
  72. //两个堆合并  
  73. BinHeap BinHeapUnion(BinHeap &H1, BinHeap &H2) {  
  74.     Position heap = NULL, pre_x = NULL, x = NULL, next_x = NULL;  
  75.     heap = BinHeapMerge(H1, H2);  
  76.     if (heap == NULL) {  
  77.         return heap;  
  78.     }  
  79.    
  80.     pre_x = NULL;  
  81.     x = heap;  
  82.     next_x = x->sibling;  
  83.    
  84.     while (next_x != NULL) {  
  85.         if ((x->degree != next_x->degree) ||//Cases 1 and 2  
  86.             ((next_x->sibling != NULL) && (next_x->degree == next_x->sibling->degree))) {  
  87.                 pre_x = x;  
  88.                 x = next_x;  
  89.         } else if (x->key <= next_x->key) {//Cases 3  
  90.             x->sibling = next_x->sibling;  
  91.             BinLink(next_x, x);  
  92.         } else {//Cases 4  
  93.             if (pre_x == NULL) {  
  94.                 heap = next_x;  
  95.             } else {  
  96.                 pre_x->sibling = next_x;  
  97.             }  
  98.             BinLink(x, next_x);  
  99.             x = next_x;  
  100.         }  
  101.         next_x = x->sibling;  
  102.     }  
  103.     return heap;  
  104. }  
  105.    
  106. //将H1, H2的根表合并成一个按度数的单调递增次序排列的链表  
  107. BinHeap BinHeapMerge(BinHeap &H1, BinHeap &H2) {  
  108.     //heap->堆的首地址,H3为指向新堆根结点  
  109.     BinHeap heap = NULL, firstHeap = NULL, secondHeap = NULL,  
  110.         pre_H3 = NULL, H3 = NULL;  
  111.    
  112.     if (H1 != NULL && H2 != NULL){  
  113.         firstHeap = H1;  
  114.         secondHeap = H2;  
  115.         //整个while,firstHeap, secondHeap, pre_H3, H3都在往后顺移  
  116.         while (firstHeap != NULL && secondHeap != NULL) {  
  117.             if (firstHeap->degree <= secondHeap->degree) {  
  118.                 H3 = firstHeap;  
  119.                 firstHeap = firstHeap->sibling;  
  120.             } else {  
  121.                 H3 = secondHeap;  
  122.                 secondHeap = secondHeap->sibling;  
  123.             }  
  124.    
  125.             if (pre_H3 == NULL) {  
  126.                 pre_H3 = H3;  
  127.                 heap = H3;  
  128.             } else {  
  129.                 pre_H3->sibling = H3;  
  130.                 pre_H3 = H3;  
  131.             }  
  132.             if (firstHeap != NULL) {  
  133.                 H3->sibling = firstHeap;  
  134.             } else {  
  135.                 H3->sibling = secondHeap;  
  136.             }  
  137.         }//while  
  138.     } else if (H1 != NULL) {  
  139.         heap = H1;  
  140.     } else {  
  141.         heap = H2;  
  142.     }  
  143.     H1 = H2 = NULL;  
  144.     return heap;  
  145. }  
  146.    
  147. //使H2成为H1的父节点  
  148. void BinLink(BinHeap &H1, BinHeap &H2) {  
  149.     H1->parent = H2;  
  150.     H1->sibling = H2->leftChild;  
  151.     H2->leftChild = H1;  
  152.     H2->degree++;  
  153. }  
  154.    
  155. //返回最小根节点的指针  
  156. BinHeap BinHeapMin(BinHeap heap) {  
  157.     Position y = NULL, x = heap;  
  158.     int min = INT_MAX;  
  159.    
  160.     while (x != NULL) {  
  161.         if (x->key < min) {  
  162.             min = x->key;  
  163.             y = x;  
  164.         }  
  165.         x = x->sibling;  
  166.     }  
  167.     return y;  
  168. }  
  169.    
  170. //抽取有最小关键字的结点  
  171. BinHeap BinHeapExtractMin(BinHeap &heap) {  
  172.     BinHeap pre_y = NULL, y = NULL, x = heap;  
  173.     int min = INT_MAX;  
  174.     while (x != NULL) {  
  175.         if (x->key < min) {  
  176.             min = x->key;  
  177.             pre_y = y;  
  178.             y = x;  
  179.         }  
  180.         x = x->sibling;  
  181.     }  
  182.    
  183.     if (y == NULL) {  
  184.         return NULL;  
  185.     }  
  186.    
  187.     if (pre_y == NULL) {  
  188.         heap = heap->sibling;  
  189.     } else {  
  190.         pre_y->sibling = y->sibling;  
  191.     }  
  192.    
  193.     //将y的子结点指针reverse  
  194.     BinHeap H2 = NULL, p = NULL;  
  195.     x = y->leftChild;  
  196.     while (x != NULL) {  
  197.         p = x;  
  198.         x = x->sibling;  
  199.         p->sibling = H2;  
  200.         H2 = p;  
  201.         p->parent = NULL;  
  202.     }  
  203.    
  204.     heap = BinHeapUnion(heap, H2);  
  205.     return y;  
  206. }  
  207.    
  208. //减少关键字的值  
  209. void BinHeapDecreaseKey(BinHeap heap, BinHeap x, int key) {  
  210.     if(key > x->key) {  
  211.         puts("new key is greaer than current key");  
  212.         exit(1); //不为降键  
  213.     }  
  214.     x->key = key;  
  215.    
  216.     BinHeap z = NULL, y = NULL;  
  217.     y = x;  
  218.     z = x->parent;  
  219.     while(z != NULL && z->key > y->key) {  
  220.         swap(z->key, y->key);  
  221.         y = z;  
  222.         z = y->parent;  
  223.     }  
  224. }  
  225.    
  226. //删除一个关键字  
  227. BinHeap BinHeapDelete(BinHeap &heap, int key) {  
  228.     BinHeap x = NULL;  
  229.     x = BinHeapFind(heap, key);  
  230.     if (x != NULL) {  
  231.         BinHeapDecreaseKey(heap, x, INT_MIN);  
  232.         return BinHeapExtractMin(heap);  
  233.     }  
  234.     return x;  
  235. }  
  236.    
  237. //找出一个关键字  
  238. BinHeap BinHeapFind(BinHeap &heap, int key) {  
  239.     Position p = NULL, x = NULL;  
  240.     p = heap;  
  241.     while (p != NULL) {  
  242.         if (p->key == key) {  
  243.             return p;  
  244.         } else {  
  245.             if((x =BinHeapFind(p->leftChild, key)) != NULL) {  
  246.                 return x;  
  247.             }  
  248.             p = p->sibling;  
  249.         }  
  250.     }  
  251.     return NULL;  
  252. }  
  253.    
  254. //打印输出堆结构  
  255. void PrintBinHeap(BinHeap heap) {  
  256.     if (NULL == heap) {  
  257.         return ;  
  258.     }  
  259.     Position p = heap;  
  260.    
  261.     while (p != NULL) {  
  262.         printf(" (");  
  263.         printf("%d", p->key);  
  264.         //显示其孩子  
  265.         if(NULL != p->leftChild) {  
  266.             PrintBinHeap(p->leftChild);  
  267.         }  
  268.         printf(") ");  
  269.    
  270.         p = p->sibling;  
  271.     }  
  272. }         
  273.    
  274. int kp1[8] = {12,  
  275.                7, 25,  
  276.               15, 28, 33, 41};  
  277.    
  278. int kp2[20] = {18,  
  279.                 3, 37,  
  280.                 6, 8, 29, 10, 44, 30, 23, 2, 48, 31, 17, 45, 32, 24, 50, 55};  
  281.    
  282. int kp4[23] = {37, 41,  
  283.                10, 28, 13, 77,  
  284.                1, 6, 16, 12, 25, 8, 14, 29, 26, 23, 18, 11, 17, 38, 42, 27};  
  285. int main() {  
  286.     BinHeap H1 = NULL;  
  287.     H1 = MakeBinHeapWithArray(kp1, 7);  
  288.     puts("第一个二叉堆H1:");  
  289.     PrintBinHeap(H1);  
  290.    
  291.     BinHeap H2 = NULL;  
  292.     H2 = MakeBinHeapWithArray(kp2, 19);  
  293.     puts("\n\n第二个二叉堆H2:");  
  294.     PrintBinHeap(H2);  
  295.    
  296.     BinHeap H3 = NULL;  
  297.     H3 = BinHeapUnion(H1, H2);  
  298.     puts("\n\n合并H1,H2后,得到H3:");  
  299.     PrintBinHeap(H3);  
  300.    
  301.     BinHeap H4 = NULL;  
  302.     H4 = MakeBinHeapWithArray(kp4, 22);  
  303.     puts("\n\n用于测试提取和删除的二叉堆H4:");  
  304.     PrintBinHeap(H4);  
  305.    
  306.     BinHeap extractNode = BinHeapExtractMin(H4);  
  307.     if (extractNode != NULL) {  
  308.         printf("\n\n抽取最小的值%d后:\n", extractNode->key);  
  309.         PrintBinHeap(H4);  
  310.     }  
  311.    
  312.     extractNode = BinHeapExtractMin(H4);  
  313.     if (extractNode != NULL) {  
  314.         printf("\n\n抽取最小的值%d后:\n", extractNode->key);  
  315.         PrintBinHeap(H4);  
  316.     }  
  317.    
  318.     extractNode = BinHeapExtractMin(H4);  
  319.     if (extractNode != NULL) {  
  320.         printf("\n\n抽取最小的值%d后:\n", extractNode->key);  
  321.         PrintBinHeap(H4);  
  322.     }  
  323.    
  324.     BinHeapDelete(H4, 12);  
  325.     puts("\n\n删除12后:");  
  326.     PrintBinHeap(H4);  
  327.     return 0;  
  328. }  

 

另外的实现参考参考②。

上述的操作的时间复杂度都是O(lgn)。 

 

斐波那契堆(Fibonacci Heap)

斐波那契堆是一种松散的二项堆,与二项堆的主要区别在于构成斐波那契堆得树可以不是二项树,并且这些树的根排列是无须的(二项堆的根结点排序是按照结点个数排序的,不是按照根结点的大小)。斐波那契堆得优势在于它对建堆、插入、抽取最小关键字、联合等操作能在O(1)的时间内完成(不涉及删除元素的操作仅需要O(1))。这是对二项堆效率的巨大改善。但由于斐波那契堆得常数因子以及程序设计上的复杂度,使它不如通常的二叉堆合适。因此,它的价值仅存在于理论意义上。斐波那契堆的另一个特点就是合并操作只发生在抽取一个结点之后,也就是说斐波那契堆的维护总是会延后的(个人根据代码理解的)。

斐波那契堆结点ADT

结点含有以下域:

1) 父节点p[x]

2) 指向任一子女的指针child[x]——结点x的子女被链接成一个环形双链表,称为x的子女表

3) 左兄弟left[x]

4) 右兄弟right[x]——当left[x] = right[x] = x时,说明x是独子。

5) 子女的个数degree[x]

6) 布尔值域mark[x]——标记是否失去了一个孩子

 

C代码  收藏代码
  1. //斐波那契结点ADT  
  2. struct FibonacciHeapNode {  
  3.     int key;       //结点  
  4.     int degree;    //度  
  5.     FibonacciHeapNode * left;  //左兄弟  
  6.     FibonacciHeapNode * right; //右兄弟  
  7.     FibonacciHeapNode * parent; //父结点  
  8.     FibonacciHeapNode * child;  //第一个孩子结点  
  9.     bool marked;           //是否被删除第1个孩子  
  10. };  
  11. typedef FibonacciHeapNode FibNode;  

 

斐波那契堆ADT

对于一个给定的斐波那契堆H,可以通过指向包含最小关键字的树根的指针min[H]来访问,这个结点被称为斐波那契堆中的最小结点。如果一个斐波那契堆H是空的,则min[H] = NIL. 在一个斐波那契堆中,所有树的根都通过left和right指针链接成一个环形的双链表,称为堆的根表。于是,指针min[H]就指向根表中具有最小关键字的结点(就是查找最小结点的操作,下文就没有再介绍了)。

C代码  收藏代码
  1. //斐波那契堆ADT  
  2. struct FibonacciHeap {  
  3.     int keyNum;   //堆中结点个数  
  4.     FibonacciHeapNode * min;//最小堆,根结点  
  5.     int maxNumOfDegree;   //最大度  
  6.     FibonacciHeapNode * * cons;//指向最大度的内存区域  
  7. };  
  8.    
  9. typedef FibonacciHeap FibHeap;  

斐波那契堆操作

1.创建斐波那契堆

创建一个空的斐波那契堆,过程MAKE-FIB-HEAP 分配并返回一个斐波那契堆对象H; 

 

C代码  收藏代码
  1. //初始化一个空的Fibonacci Heap  
  2. FibHeap * FibHeapMake() {  
  3.     FibHeap * heap = NULL;  
  4.     heap = (FibHeap *) malloc(sizeof(FibHeap));  
  5.     if (NULL == heap) {  
  6.         puts("Out of Space!!");  
  7.         exit(1);  
  8.     }  
  9.     memset(heap, 0, sizeof(FibHeap));  
  10.     return heap;  
  11. }  
  12.    
  13. //初始化结点x  
  14. FibNode * FibHeapNodeMake() {  
  15.     FibNode * x = NULL;  
  16.     x = (FibNode *) malloc(sizeof(FibNode));  
  17.     if (NULL == x) {  
  18.         puts("Out of Space!!");  
  19.         exit(1);  
  20.     }  
  21.     memset(x, 0, sizeof(FibNode));  
  22.     x->left = x->right = x;  
  23.     return x;  
  24. }  

 

2.插入一个结点

要出人一个结点x,对结点的各域初始化,赋值,然后构造自身的环形双向链表后,将x加入H的根表中。 也就是说,结点x 成为一棵单结点的最小堆有序树,同时就是斐波那契堆中一棵无序树而且在根表最小结点的左边。

     如图是将关键字为21的结点插入斐波那契堆。该结点自成一棵最小堆有序树,从而被加入到根表中,成为根的左兄弟。 

 

 

C代码  收藏代码
  1. //堆结点x插入fibonacci heap中  
  2. void FibHeapInsert(FibHeap * heap, FibNode * x) {  
  3.     if (0 == heap->keyNum) {  
  4.         heap->min = x;  
  5.     } else {  
  6.         FibNodeAdd(x, heap->min);  
  7.         x->parent = NULL;  
  8.         if (x->key < heap->min->key) {  
  9.             heap->min = x;  
  10.         }  
  11.     }  
  12.     heap->keyNum++;  
  13. }  
  14.    
  15. //将数组内的值插入Fibonacci Heap  
  16. void FibHeapInsertKeys(FibHeap * heap, int keys[], int keyNum) {  
  17.     for (int i = 0; i < keyNum; i++) {  
  18.         FibHeapInsertKey(heap, keys[i]);  
  19.     }  
  20. }  
  21.    
  22. //将值插入Fibonacci Heap  
  23. static void FibHeapInsertKey(FibHeap * heap, int key) {  
  24.     FibNode * x = NULL;  
  25.     x = FibHeapNodeMake();  
  26.     x->key = key;  
  27.     FibHeapInsert(heap, x);  
  28. }  

 

3.合并两个堆

不同于二项堆,这个操作在斐波那契堆里非常简单。仅仅简单地将H1和H2的两根表串联,然后确定一个新的最小结点。

4.抽取(删除)最小结点

抽取最小结点的操作是斐波那契堆最复杂的操作,就是过程比较多,下面一步一步介绍。

1)将要抽取最小结点的子树都直接串联在根表中,如图(b)

2)然后就是合并所有degree相等的树,直到没有相等的degree的树。

(1)先定义一个数组A,数组A保存的根表子树中的头结点,A[i]=y表示树 y 的degree值是 i 。这样就可以得到数组A的长度就等于当前斐波那契堆中degree的最大值。

(2)当发现两个相等的degree的树,就执行合并操作。“发现”是这样得到的——向右遍历根表的结点,得到当前子树的degree的值,如果该degree值在A数组已经有元素则就是degree相等,就可以合并了。如图(e)(f)就是将degree等于 1的两个子树合并。

 

 

C代码  收藏代码
  1. //抽取最小结点  
  2. FibNode * FibHeapExtractMin(FibHeap * heap) {  
  3.     FibNode * x = NULL, * z = heap->min;  
  4.     if (z != NULL) {  
  5.    
  6.         //删除z的每一个孩子  
  7.         while (NULL != z->child) {  
  8.             x = z->child;  
  9.             FibNodeRemove(x);  
  10.             if (x->right == x) {  
  11.                 z->child = NULL;  
  12.             } else {  
  13.                 z->child = x->right;  
  14.             }  
  15.             FibNodeAdd(x, z);//add x to the root list heap  
  16.             x->parent = NULL;  
  17.         }  
  18.    
  19.         FibNodeRemove(z);  
  20.         if (z->right == z) {  
  21.             heap->min = NULL;  
  22.         } else {  
  23.             heap->min = z->right;  
  24.             FibHeapConsolidate(heap);  
  25.         }  
  26.         heap->keyNum--;  
  27.     }  
  28.     return z;  
  29. }  
  30.    
  31. //合并左右相同度数的二项树  
  32. void FibHeapConsolidate(FibHeap * heap) {  
  33.     int D, d;  
  34.     FibNode * w = heap->min, * x = NULL, * y = NULL;  
  35.     FibHeapConsMake(heap);//开辟哈希所用空间  
  36.     D = heap->maxNumOfDegree + 1;  
  37.     for (int i = 0; i < D; i++) {  
  38.         *(heap->cons + i) = NULL;  
  39.     }  
  40.    
  41.     //合并相同度的根节点,使每个度数的二项树唯一  
  42.     while (NULL != heap->min) {  
  43.         x = FibHeapMinRemove(heap);  
  44.         d = x->degree;  
  45.         while (NULL != *(heap->cons + d)) {  
  46.             y = *(heap->cons + d);  
  47.             if (x->key > y->key) {//根结点key最小  
  48.                 swap(x, y);  
  49.             }  
  50.             FibHeapLink(heap, y, x);  
  51.             *(heap->cons + d) = NULL;  
  52.             d++;  
  53.         }  
  54.         *(heap->cons + d) = x;  
  55.     }  
  56.     heap->min = NULL;//原有根表清除  
  57.    
  58.     //将heap->cons中结点都重新加到根表中,且找出最小根  
  59.     for (int i = 0; i < D; i++) {  
  60.         if (*(heap->cons + i) != NULL) {  
  61.             if (NULL == heap->min) {  
  62.                 heap->min = *(heap->cons + i);  
  63.             } else {  
  64.                 FibNodeAdd(*(heap->cons + i), heap->min);  
  65.                 if ((*(heap->cons + i))->key < heap->min->key) {  
  66.                     heap->min = *(heap->cons + i);  
  67.                 }//if(<)  
  68.             }//if-else(==)  
  69.         }//if(!=)  
  70.     }//for(i)  
  71. }  
  72.    
  73. //将x根结点链接到y根结点  
  74. void FibHeapLink(FibHeap * heap, FibNode * x, FibNode *y) {  
  75.     FibNodeRemove(x);  
  76.     if (NULL == y->child) {  
  77.         y->child = x;  
  78.     } else {  
  79.         FibNodeAdd(x, y->child);  
  80.     }  
  81.     x->parent = y;  
  82.     y->degree++;  
  83.     x->marked = false;  
  84. }  
  85.    
  86. //开辟FibHeapConsolidate函数哈希所用空间  
  87. static void FibHeapConsMake(FibHeap * heap) {  
  88.     int old = heap->maxNumOfDegree;  
  89.     heap->maxNumOfDegree = int(log(heap->keyNum * 1.0) / log(2.0)) + 1;  
  90.     if (old < heap->maxNumOfDegree) {  
  91.         //因为度为heap->maxNumOfDegree可能被合并,所以要maxNumOfDegree + 1  
  92.         heap->cons = (FibNode **) realloc(heap->cons,  
  93.             sizeof(FibHeap *) * (heap->maxNumOfDegree + 1));  
  94.         if (NULL == heap->cons) {  
  95.             puts("Out of Space!");  
  96.             exit(1);  
  97.         }  
  98.     }  
  99. }  
  100.    
  101. //将堆的最小结点移出,并指向其有兄弟  
  102. static FibNode *FibHeapMinRemove(FibHeap * heap) {  
  103.     FibNode *min = heap->min;  
  104.     if (heap->min == min->right) {  
  105.         heap->min = NULL;  
  106.     } else {  
  107.         FibNodeRemove(min);  
  108.         heap->min = min->right;  
  109.     }  
  110.     min->left = min->right = min;  
  111.     return min;  
  112. }  

 

 

5.减小一个关键字

减小一个关键字的字,会破坏最小堆的性质,所以要进行最小堆维护。因为斐波那契支持减小关键字和删除结点操作,所以斐波那契堆的子树就不一定是二项树了。

减小一个关键字主要进行两个步骤:

1)减小关键字,如果破坏最小堆性质,则将该结点a直接从原来的树移除直接串联在根表中,并将父结点p的mark属性设置成长true。

2)进行级联剪枝:如果当前父结点p的mark属性是true,且p的父结点pp的mark属性也是true,那么将p从pp移除加入到根表中

下图中,黑色的结点表示其mark属性为true,(a)(b)将关键字46减少为15,没有发生级联剪枝;(c,d,e)将35减小为5,发生了级联剪枝操作

C代码  收藏代码
  1. //减小一个关键字  
  2. void FibHeapDecrease(FibHeap * heap, FibNode * x, int key) {  
  3.     FibNode * y = x->parent;  
  4.     if (x->key < key) {  
  5.         puts("new key is greater than current key!");  
  6.         exit(1);  
  7.     }  
  8.     x->key = key;  
  9.    
  10.     if (NULL != y && x->key < y->key) {  
  11.         //破坏了最小堆性质,需要进行级联剪切操作  
  12.         FibHeapCut(heap, x, y);  
  13.         FibHeapCascadingCut(heap, y);  
  14.     }  
  15.     if (x->key < heap->min->key) {  
  16.         heap->min = x;  
  17.     }  
  18. }  
  19.    
  20. //切断x与父节点y之间的链接,使x成为一个根  
  21. static void FibHeapCut(FibHeap * heap, FibNode * x, FibNode * y) {  
  22.     FibNodeRemove(x);  
  23.     renewDegree(y, x->degree);  
  24.     if (x == x->right) {  
  25.         y->child = NULL;  
  26.     } else {  
  27.         y->child = x->right;  
  28.     }  
  29.     x->parent = NULL;  
  30.     x->left = x->right = x;  
  31.     x->marked = false;  
  32.     FibNodeAdd(x, heap->min);  
  33. }  
  34.    
  35. //级联剪切  
  36. static void FibHeapCascadingCut(FibHeap * heap, FibNode * y) {  
  37.     FibNode * z = y->parent;  
  38.     if (NULL != z) {  
  39.         if (y->marked == false) {  
  40.             y->marked = true;  
  41.         } else {  
  42.             FibHeapCut(heap, y, z);  
  43.             FibHeapCascadingCut(heap, z);  
  44.         }  
  45.     }  
  46. }  
  47.    
  48. //修改度数  
  49. void renewDegree(FibNode * parent, int degree) {  
  50.     parent->degree -= degree;  
  51.     if (parent-> parent != NULL) {  
  52.         renewDegree(parent->parent, degree);  
  53.     }  
  54. }  

 6.删除一个结点

删除结点X,首先将X的关键字值减小到比最小结点的值更小(调用减小一个关键字的过程),然后删除(抽取)最小结点就可以了。

C代码  收藏代码
  1. //删除结点  
  2. void FibHeapDelete(FibHeap * heap, FibNode * x) {  
  3.     FibHeapDecrease(heap, x, INT_MIN);  
  4.     FibHeapExtractMin(heap);  
  5. }  

 

斐波那契堆完整实现

 

Cpp代码  收藏代码
  1. //说明:  
  2. //代码中Fibonacci Heap 用变量heap表示  
  3. //结点通常用x,y等表示  
  4. #include<iostream>  
  5. #include<cstdio>  
  6. #include<cstdlib>  
  7. #include<cmath>  
  8. #include<climits>  
  9. using namespace std;  
  10.    
  11. //斐波那契结点ADT  
  12. struct FibonacciHeapNode {  
  13.     int key;       //结点  
  14.     int degree;    //度  
  15.     FibonacciHeapNode * left;  //左兄弟  
  16.     FibonacciHeapNode * right; //右兄弟  
  17.     FibonacciHeapNode * parent; //父结点  
  18.     FibonacciHeapNode * child;  //第一个孩子结点  
  19.     bool marked;           //是否被删除第1个孩子  
  20. };  
  21.    
  22. typedef FibonacciHeapNode FibNode;  
  23.    
  24. //斐波那契堆ADT  
  25. struct FibonacciHeap {  
  26.     int keyNum;   //堆中结点个数  
  27.     FibonacciHeapNode * min;//最小堆,根结点  
  28.     int maxNumOfDegree;   //最大度  
  29.     FibonacciHeapNode * * cons;//指向最大度的内存区域  
  30. };  
  31.    
  32. typedef FibonacciHeap FibHeap;  
  33.    
  34. /*****************函数申明*************************/  
  35. //将x从双链表移除  
  36. inline void FibNodeRemove(FibNode * x);  
  37.    
  38. //将x堆结点加入y结点之前(循环链表中)  
  39. void FibNodeAdd(FibNode * x, FibNode * y);  
  40.    
  41. //初始化一个空的Fibonacci Heap  
  42. FibHeap * FibHeapMake() ;  
  43.    
  44. //初始化结点x  
  45. FibNode * FibHeapNodeMake();  
  46.    
  47. //堆结点x插入fibonacci heap中  
  48. void FibHeapInsert(FibHeap * heap, FibNode * x);  
  49.    
  50. //将数组内的值插入Fibonacci Heap  
  51. void FibHeapInsertKeys(FibHeap * heap, int keys[], int keyNum);  
  52.    
  53. //将值插入Fibonacci Heap  
  54. static void FibHeapInsertKey(FibHeap * heap, int key);  
  55.    
  56. //抽取最小结点  
  57. FibNode * FibHeapExtractMin(FibHeap * heap);  
  58.    
  59. //合并左右相同度数的二项树  
  60. void FibHeapConsolidate(FibHeap * heap);  
  61.    
  62. //将x根结点链接到y根结点  
  63. void FibHeapLink(FibHeap * heap, FibNode * x, FibNode *y);  
  64.    
  65. //开辟FibHeapConsolidate函数哈希所用空间  
  66. static void FibHeapConsMake(FibHeap * heap);  
  67.    
  68. //将堆的最小结点移出,并指向其有兄弟  
  69. static FibNode *FibHeapMinRemove(FibHeap * heap);  
  70.    
  71. //减小一个关键字  
  72. void FibHeapDecrease(FibHeap * heap, FibNode * x, int key);  
  73.    
  74. //切断x与父节点y之间的链接,使x成为一个根  
  75. static void FibHeapCut(FibHeap * heap, FibNode * x, FibNode * y);  
  76.    
  77. //级联剪切  
  78. static void FibHeapCascadingCut(FibHeap * heap, FibNode * y);  
  79.    
  80. //修改度数  
  81. void renewDegree(FibNode * parent, int degree);  
  82.    
  83. //删除结点  
  84. void FibHeapDelete(FibHeap * heap, FibNode * x);  
  85.    
  86. //堆内搜索关键字  
  87. FibNode * FibHeapSearch(FibHeap * heap, int key);  
  88.    
  89. //被FibHeapSearch调用  
  90. static FibNode * FibNodeSearch(FibNode * x, int key);  
  91.    
  92. //销毁堆  
  93. void FibHeapDestory(FibHeap * heap);  
  94.    
  95. //被FibHeapDestory调用  
  96. static void FibNodeDestory(FibNode * x);  
  97.    
  98. //输出打印堆  
  99. static void FibHeapPrint(FibHeap * heap);  
  100.    
  101. //被FibHeapPrint调用  
  102. static void FibNodePrint(FibNode * x);  
  103. /************************************************/  
  104.    
  105. //将x从双链表移除  
  106. inline void FibNodeRemove(FibNode * x) {  
  107.     x->left->right = x->right;  
  108.     x->right->left = x->left;  
  109. }  
  110.    
  111. /* 
  112. 将x堆结点加入y结点之前(循环链表中) 
  113.     a …… y 
  114.     a …… x …… y 
  115. */  
  116. inline void FibNodeAdd(FibNode * x, FibNode * y) {  
  117.     x->left = y->left;  
  118.     y->left->right = x;  
  119.     x->right = y;  
  120.     y->left = x;  
  121. }  
  122.    
  123. //初始化一个空的Fibonacci Heap  
  124. FibHeap * FibHeapMake() {  
  125.     FibHeap * heap = NULL;  
  126.     heap = (FibHeap *) malloc(sizeof(FibHeap));  
  127.     if (NULL == heap) {  
  128.         puts("Out of Space!!");  
  129.         exit(1);  
  130.     }  
  131.     memset(heap, 0, sizeof(FibHeap));  
  132.     return heap;  
  133. }  
  134.    
  135. //初始化结点x  
  136. FibNode * FibHeapNodeMake() {  
  137.     FibNode * x = NULL;  
  138.     x = (FibNode *) malloc(sizeof(FibNode));  
  139.     if (NULL == x) {  
  140.         puts("Out of Space!!");  
  141.         exit(1);  
  142.     }  
  143.     memset(x, 0, sizeof(FibNode));  
  144.     x->left = x->right = x;  
  145.     return x;  
  146. }  
  147.    
  148. //堆结点x插入fibonacci heap中  
  149. void FibHeapInsert(FibHeap * heap, FibNode * x) {  
  150.     if (0 == heap->keyNum) {  
  151.         heap->min = x;  
  152.     } else {  
  153.         FibNodeAdd(x, heap->min);  
  154.         x->parent = NULL;  
  155.         if (x->key < heap->min->key) {  
  156.             heap->min = x;  
  157.         }  
  158.     }  
  159.     heap->keyNum++;  
  160. }  
  161.    
  162. //将数组内的值插入Fibonacci Heap  
  163. void FibHeapInsertKeys(FibHeap * heap, int keys[], int keyNum) {  
  164.     for (int i = 0; i < keyNum; i++) {  
  165.         FibHeapInsertKey(heap, keys[i]);  
  166.     }  
  167. }  
  168.    
  169. //将值插入Fibonacci Heap  
  170. static void FibHeapInsertKey(FibHeap * heap, int key) {  
  171.     FibNode * x = NULL;  
  172.     x = FibHeapNodeMake();  
  173.     x->key = key;  
  174.     FibHeapInsert(heap, x);  
  175. }  
  176.    
  177. //抽取最小结点  
  178. FibNode * FibHeapExtractMin(FibHeap * heap) {  
  179.     FibNode * x = NULL, * z = heap->min;  
  180.     if (z != NULL) {  
  181.    
  182.         //删除z的每一个孩子  
  183.         while (NULL != z->child) {  
  184.             x = z->child;  
  185.             FibNodeRemove(x);  
  186.             if (x->right == x) {  
  187.                 z->child = NULL;  
  188.             } else {  
  189.                 z->child = x->right;  
  190.             }  
  191.             FibNodeAdd(x, z);//add x to the root list heap  
  192.             x->parent = NULL;  
  193.         }  
  194.    
  195.         FibNodeRemove(z);  
  196.         if (z->right == z) {  
  197.             heap->min = NULL;  
  198.         } else {  
  199.             heap->min = z->right;  
  200.             FibHeapConsolidate(heap);  
  201.         }  
  202.         heap->keyNum--;  
  203.     }  
  204.     return z;  
  205. }  
  206.    
  207. //合并左右相同度数的二项树  
  208. void FibHeapConsolidate(FibHeap * heap) {  
  209.     int D, d;  
  210.     FibNode * w = heap->min, * x = NULL, * y = NULL;  
  211.     FibHeapConsMake(heap);//开辟哈希所用空间  
  212.     D = heap->maxNumOfDegree + 1;  
  213.     for (int i = 0; i < D; i++) {  
  214.         *(heap->cons + i) = NULL;  
  215.     }  
  216.    
  217.     //合并相同度的根节点,使每个度数的二项树唯一  
  218.     while (NULL != heap->min) {  
  219.         x = FibHeapMinRemove(heap);  
  220.         d = x->degree;  
  221.         while (NULL != *(heap->cons + d)) {  
  222.             y = *(heap->cons + d);  
  223.             if (x->key > y->key) {//根结点key最小  
  224.                 swap(x, y);  
  225.             }  
  226.             FibHeapLink(heap, y, x);  
  227.             *(heap->cons + d) = NULL;  
  228.             d++;  
  229.         }  
  230.         *(heap->cons + d) = x;  
  231.     }  
  232.     heap->min = NULL;//原有根表清除  
  233.    
  234.     //将heap->cons中结点都重新加到根表中,且找出最小根  
  235.     for (int i = 0; i < D; i++) {  
  236.         if (*(heap->cons + i) != NULL) {  
  237.             if (NULL == heap->min) {  
  238.                 heap->min = *(heap->cons + i);  
  239.             } else {  
  240.                 FibNodeAdd(*(heap->cons + i), heap->min);  
  241.                 if ((*(heap->cons + i))->key < heap->min->key) {  
  242.                     heap->min = *(heap->cons + i);  
  243.                 }//if(<)  
  244.             }//if-else(==)  
  245.         }//if(!=)  
  246.     }//for(i)  
  247. }  
  248.    
  249. //将x根结点链接到y根结点  
  250. void FibHeapLink(FibHeap * heap, FibNode * x, FibNode *y) {  
  251.     FibNodeRemove(x);  
  252.     if (NULL == y->child) {  
  253.         y->child = x;  
  254.     } else {  
  255.         FibNodeAdd(x, y->child);  
  256.     }  
  257.     x->parent = y;  
  258.     y->degree++;  
  259.     x->marked = false;  
  260. }  
  261.    
  262. //开辟FibHeapConsolidate函数哈希所用空间  
  263. static void FibHeapConsMake(FibHeap * heap) {  
  264.     int old = heap->maxNumOfDegree;  
  265.     heap->maxNumOfDegree = int(log(heap->keyNum * 1.0) / log(2.0)) + 1;  
  266.     if (old < heap->maxNumOfDegree) {  
  267.         //因为度为heap->maxNumOfDegree可能被合并,所以要maxNumOfDegree + 1  
  268.         heap->cons = (FibNode **) realloc(heap->cons,  
  269.             sizeof(FibHeap *) * (heap->maxNumOfDegree + 1));  
  270.         if (NULL == heap->cons) {  
  271.             puts("Out of Space!");  
  272.             exit(1);  
  273.         }  
  274.     }  
  275. }  
  276.    
  277. //将堆的最小结点移出,并指向其有兄弟  
  278. static FibNode *FibHeapMinRemove(FibHeap * heap) {  
  279.     FibNode *min = heap->min;  
  280.     if (heap->min == min->right) {  
  281.         heap->min = NULL;  
  282.     } else {  
  283.         FibNodeRemove(min);  
  284.         heap->min = min->right;  
  285.     }  
  286.     min->left = min->right = min;  
  287.     return min;  
  288. }  
  289.    
  290. //减小一个关键字  
  291. void FibHeapDecrease(FibHeap * heap, FibNode * x, int key) {  
  292.     FibNode * y = x->parent;  
  293.     if (x->key < key) {  
  294.         puts("new key is greater than current key!");  
  295.         exit(1);  
  296.     }  
  297.     x->key = key;  
  298.    
  299.     if (NULL != y && x->key < y->key) {  
  300.         //破坏了最小堆性质,需要进行级联剪切操作  
  301.         FibHeapCut(heap, x, y);  
  302.         FibHeapCascadingCut(heap, y);  
  303.     }  
  304.     if (x->key < heap->min->key) {  
  305.         heap->min = x;  
  306.     }  
  307. }  
  308.    
  309. //切断x与父节点y之间的链接,使x成为一个根  
  310. static void FibHeapCut(FibHeap * heap, FibNode * x, FibNode * y) {  
  311.     FibNodeRemove(x);  
  312.     renewDegree(y, x->degree);  
  313.     if (x == x->right) {  
  314.         y->child = NULL;  
  315.     } else {  
  316.         y->child = x->right;  
  317.     }  
  318.     x->parent = NULL;  
  319.     x->left = x->right = x;  
  320.     x->marked = false;  
  321.     FibNodeAdd(x, heap->min);  
  322. }  
  323.    
  324. //级联剪切  
  325. static void FibHeapCascadingCut(FibHeap * heap, FibNode * y) {  
  326.     FibNode * z = y->parent;  
  327.     if (NULL != z) {  
  328.         if (y->marked == false) {  
  329.             y->marked = true;  
  330.         } else {  
  331.             FibHeapCut(heap, y, z);  
  332.             FibHeapCascadingCut(heap, z);  
  333.         }  
  334.     }  
  335. }  
  336.    
  337. //修改度数  
  338. void renewDegree(FibNode * parent, int degree) {  
  339.     parent->degree -= degree;  
  340.     if (parent-> parent != NULL) {  
  341.         renewDegree(parent->parent, degree);  
  342.     }  
  343. }  
  344.    
  345. //删除结点  
  346. void FibHeapDelete(FibHeap * heap, FibNode * x) {  
  347.     FibHeapDecrease(heap, x, INT_MIN);  
  348.     FibHeapExtractMin(heap);  
  349. }  
  350.    
  351. //堆内搜索关键字  
  352. FibNode * FibHeapSearch(FibHeap * heap, int key) {  
  353.     return FibNodeSearch(heap->min, key);  
  354. }  
  355.    
  356. //被FibHeapSearch调用  
  357. static FibNode * FibNodeSearch(FibNode * x, int key) {  
  358.     FibNode * w = x, * y = NULL;  
  359.     if (x != NULL) {  
  360.         do {  
  361.             if (w->key == key) {  
  362.                 y = w;  
  363.                 break;  
  364.             } else if (NULL != (y = FibNodeSearch(w->child, key))) {  
  365.                 break;  
  366.             }  
  367.             w = w->right;  
  368.         } while (w != x);  
  369.     }  
  370.     return y;  
  371. }  
  372.    
  373. //销毁堆  
  374. void FibHeapDestory(FibHeap * heap) {  
  375.     FibNodeDestory(heap->min);  
  376.     free(heap);  
  377.     heap = NULL;  
  378. }  
  379.    
  380. //被FibHeapDestory调用  
  381. static void FibNodeDestory(FibNode * x) {  
  382.     FibNode * p = x, *q = NULL;  
  383.     while (p != NULL) {  
  384.         FibNodeDestory(p->child);  
  385.         q = p;  
  386.         if (p -> left == x) {  
  387.             p = NULL;  
  388.         } else {  
  389.             p = p->left;  
  390.         }  
  391.         free(q->right);  
  392.     }  
  393. }  
  394.    
  395. //输出打印堆  
  396. static void FibHeapPrint(FibHeap * heap) {  
  397.     printf("The keyNum = %d\n", heap->keyNum);  
  398.     FibNodePrint(heap->min);  
  399.     puts("\n");  
  400. };  
  401.    
  402. //被FibHeapPrint调用  
  403. static void FibNodePrint(FibNode * x) {  
  404.     FibNode * p = NULL;  
  405.     if (NULL == x) {  
  406.         return ;  
  407.     }  
  408.     p = x;  
  409.     do {  
  410.         printf(" (");  
  411.         printf("%d", p->key);  
  412.         if (p->child != NULL) {  
  413.             FibNodePrint(p->child);  
  414.         }  
  415.         printf(") ");  
  416.         p = p->left;  
  417.     }while (x != p);  
  418. }  
  419.    
  420. int keys[10] = {1, 2, 3, 4, 5, 6, 7, 9, 10, 11};  
  421.    
  422. int main() {  
  423.     FibHeap * heap = NULL;  
  424.     FibNode * x = NULL;  
  425.     heap = FibHeapMake();  
  426.     FibHeapInsertKeys(heap, keys, 10);  
  427.     FibHeapPrint(heap);  
  428.    
  429.     x = FibHeapExtractMin(heap);  
  430.     printf("抽取最小值%d之后:\n", x->key);  
  431.     FibHeapPrint(heap);  
  432.    
  433.     x = FibHeapSearch(heap, 11);  
  434.     if (NULL != x) {  
  435.         printf("查找%d成功,", x->key);  
  436.         FibHeapDecrease(heap, x, 8);  
  437.         printf("减小到%d后:\n", x->key);  
  438.         FibHeapPrint(heap);  
  439.     }  
  440.    
  441.     x = FibHeapSearch(heap, 7);  
  442.     if (NULL != x) {  
  443.         printf("删除%d成功:\n", x->key);  
  444.         FibHeapDelete(heap, x);  
  445.         FibHeapPrint(heap);  
  446.     }  
  447.    
  448.     FibHeapDestory(heap);  
  449.     return 0;  
  450. }  

 

Pairing Heap

斐波那契堆主要有两个缺点:编程实现难度较大和实际效率没有理论的那么快(由于它的存储结构和四个指针)。Pairing Heap的提出就是弥补斐波那契堆的两个缺点——编程简单操作的时间复杂度和斐波那契堆一样。

Pairing Heap其实就是一个具有堆(最大堆或最小堆)性质的树,它的特性不是由它的结构决定的,而是由于它的操作(插入,合并,减小一个关键字等)决定的。

 

Pairing Heap的ADT

 

C代码  收藏代码
  1. typedef struct PairingHeapNode  
  2. {  
  3.     int                         key;  
  4.     struct  PairingHeapNode*    child;  
  5.     struct  PairingHeapNode*    sibling;  
  6.     struct  PairingHeapNode*    prev;  
  7.   
  8. }PairHeap;  

 

Pairing Heap 的操作

注意:图解过程是以最大堆来演示的,但是代码是以最小堆来写的,见谅!

1.合并两个子堆


C代码  收藏代码
  1. static PairHeap* merge_subheaps(PairHeap *p, PairHeap *q)  
  2. {  
  3.     if(q == NULL)  
  4.         return p;  
  5.     else if(p->key <= q->key)  
  6.     {  
  7.         q->prev = p;  
  8.         p->sibling = q->sibling;  
  9.         if(p->sibling != NULL)  
  10.             p->sibling->prev = p;  
  11.   
  12.         q->sibling = p->child;  
  13.         if(q->sibling != NULL)  
  14.             q->sibling->prev = q;  
  15.   
  16.         p->child = q;  
  17.         return p;  
  18.     }  
  19.     else  
  20.     {  
  21.         q->prev = p->prev;  
  22.         p->prev = q;  
  23.         p->sibling = q->child;  
  24.         if(p->sibling != NULL)  
  25.             p->sibling->prev = p;  
  26.   
  27.         q->child = p;  
  28.         return q;  
  29.     }  
  30. }  

 

 

2. 插入一个结点


C代码  收藏代码
  1. PairHeap* PairHeap_insert(PairHeap *p, int key)  
  2. {  
  3.     PairHeap *node;  
  4.   
  5.     node = (PairHeap*)malloc(sizeof(*node));  
  6.     if(node == NULL)  
  7.         return NULL;  
  8.   
  9.     node->key = key;  
  10.     node->child = node->sibling = node->prev = NULL;  
  11.   
  12.     if(p == NULL)  
  13.         return node;  
  14.     else  
  15.         return merge_subheaps(p, node);  
  16. }  

3.增加(减小)一个关键字


 


C代码  收藏代码
  1. PairHeap* PairHeap_DecreaseKey(PairHeap *p, PairHeap *pos, int d)  
  2. {  
  3.     if(d < 0)  
  4.         return p;  
  5.   
  6.     pos->key = pos->key - d;  
  7.     if(p == pos)  
  8.         return p;  
  9.   
  10.     if(pos->sibling != NULL)  
  11.         pos->sibling->prev = pos->prev;  
  12.   
  13.     if(pos->prev->child = pos)  
  14.         pos->prev->child = pos->sibling;  
  15.     else  
  16.         pos->prev->sibling = p->sibling;  
  17.   
  18.     p->sibling = NULL;  
  19.     return merge_subheaps(p, pos);  
  20. }   

5.删除最小结点


 

 

Pairing Heap完整实现

 

C代码  收藏代码
  1. #include <stdlib.h>  
  2.   
  3. typedef struct PairingHeapNode  
  4. {  
  5.     int                         key;  
  6.     struct  PairingHeapNode*    child;  
  7.     struct  PairingHeapNode*    sibling;  
  8.     struct  PairingHeapNode*    prev;  
  9.   
  10. }PairHeap;  
  11.   
  12. static PairHeap* merge_subheaps(PairHeap *p, PairHeap *q);  
  13. static PairHeap* combine_siblings(PairHeap *p);  
  14.   
  15. PairHeap* PairHeap_insert(PairHeap *p, int key)  
  16. {  
  17.     PairHeap *node;  
  18.   
  19.     node = (PairHeap*)malloc(sizeof(*node));  
  20.     if(node == NULL)  
  21.         return NULL;  
  22.   
  23.     node->key = key;  
  24.     node->child = node->sibling = node->prev = NULL;  
  25.   
  26.     if(p == NULL)  
  27.         return node;  
  28.     else  
  29.         return merge_subheaps(p, node);  
  30. }  
  31.   
  32. PairHeap* PairHeap_DecreaseKey(PairHeap *p, PairHeap *pos, int d)  
  33. {  
  34.     if(d < 0)  
  35.         return p;  
  36.   
  37.     pos->key = pos->key - d;  
  38.     if(p == pos)  
  39.         return p;  
  40.   
  41.     if(pos->sibling != NULL)  
  42.         pos->sibling->prev = pos->prev;  
  43.   
  44.     if(pos->prev->child = pos)  
  45.         pos->prev->child = pos->sibling;  
  46.     else  
  47.         pos->prev->sibling = p->sibling;  
  48.   
  49.     p->sibling = NULL;  
  50.     return merge_subheaps(p, pos);  
  51. }  
  52.   
  53. PairHeap* PairHeap_DeleteMin(int *key, PairHeap *p)  
  54. {  
  55.     PairHeap *new_root;  
  56.   
  57.     if(p == NULL)  
  58.         return NULL;  
  59.     else  
  60.     {  
  61.         *key = p->key;  
  62.         if(p->child != NULL)  
  63.             new_root = combine_siblings(p->child);  
  64.   
  65.         free(p);  
  66.     }  
  67.     return new_root;  
  68. }  
  69.   
  70. static PairHeap* combine_siblings(PairHeap *p)  
  71. {  
  72.     PairHeap *tree_array[1024];  
  73.     int i, count;  
  74.   
  75.     if(p->sibling == NULL)  
  76.         return p;  
  77.   
  78.     for(count = 0; p != NULL; count++)  
  79.     {  
  80.         tree_array[count] = p;  
  81.         p->prev->sibling = NULL;  
  82.         p = p->sibling;  
  83.     }  
  84.     tree_array[count] = NULL;  
  85.   
  86.     for(i = 1; i < count; i++)  
  87.         tree_array[i] = merge_subheaps(tree_array[i-1], tree_array[i]);  
  88.   
  89.     return tree_array[count-1];  
  90. }  
  91.   
  92. static PairHeap* merge_subheaps(PairHeap *p, PairHeap *q)  
  93. {  
  94.     if(q == NULL)  
  95.         return p;  
  96.     else if(p->key <= q->key)  
  97.     {  
  98.         q->prev = p;  
  99.         p->sibling = q->sibling;  
  100.         if(p->sibling != NULL)  
  101.             p->sibling->prev = p;  
  102.   
  103.         q->sibling = p->child;  
  104.         if(q->sibling != NULL)  
  105.             q->sibling->prev = q;  
  106.   
  107.         p->child = q;  
  108.         return p;  
  109.     }  
  110.     else  
  111.     {  
  112.         q->prev = p->prev;  
  113.         p->prev = q;  
  114.         p->sibling = q->child;  
  115.         if(p->sibling != NULL)  
  116.             p->sibling->prev = p;  
  117.   
  118.         q->child = p;  
  119.         return q;  
  120.     }  
  121. }  

 

 

小结

终于到小结了,这篇文章写得比较吃力,如斐波那契堆的“减小一个关键字”的操作我就对使用级联剪切还没有找到解释,还有还没有找到Pairing Heap的定义,唯独只有自己理解和揣测(比如pairing heap没有明确的定义,本人个人理解pairing heap的独特性一定在他的操作,即行为决定特征)但总归还是相对完整,希望能对你有说帮助。如果你有任何建议、批评或补充,还请你不吝提出,不甚感激。更多参考请移步互联网。

 

 

 

 

参考:

酷~行天下

http://mindlee.net/2011/09/26/binomial-heaps/

Björn B. Brandenburg:
http://www.cs.unc.edu/~bbb/#binomial_heaps

酷~行天下
http://mindlee.net/2011/09/29/fibonacci-heaps/

Adoo's blog 
http://www.roading.org/algorithm/introductiontoalgorithm/%E6%96%90%E6%B3%A2%E9%82%A3%E5%A5%91%E5%A0%86fibonacci-heaps.html

Golden_Shadow
http://blog.csdn.net/golden_shadow/article/details/6216921

Sartaj Sahni:
http://www.cise.ufl.edu/~sahni/dsaaj/enrich/c13/pairing.htm

⑦C++ template Fibonacci heap, with demonstration
http://ideone.com/9jYnv

"The pairing
heap: a new form of self-adjusting heap"

http://www.cs.cmu.edu/afs/cs.cmu.edu/user/sleator/www/papers/pairing-heaps.pdf

Vikas Tandi :
http://programmingpraxis.com/2009/08/14/pairing-heaps/

http://www.cise.ufl.edu/~sahni/cop5536/slides/lec156.pdf

⑩+1: 算法导论和维基百科

抱歉!评论已关闭.