二项堆的概念与代码实现

这篇博文主要介绍二项堆。

 

下面对这三者进行对比

其中(*)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

 

 

二项dui的操作

1)创建二项堆 


2)寻找最小关键字

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


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), 四个过程变化情况:

 

 4)插入一个结点

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


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

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

 

 6)减小关键字的值

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

 

7)删除一个关键字

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


二项树的完整实现

 

  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. }  

 


  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值