这篇博文主要介绍二项堆。
下面对这三者进行对比
其中(*)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 的结点共有个结点(从上图就可以看出结点是按二项分布的(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)删除一个关键字
删除一个关键字转换为前面的过程——将该关键字修改为最小值(要维护最小堆的性质),然后变成删除最小关键字的过程。
二项树的完整实现
- #include<iostream>
- #include<cstdio>
- #include<cstring>
- #include<cstdlib>
- #include<climits>
- using namespace std;
- typedef struct BinHeapNode BinNode;
- typedef struct BinHeapNode * BinHeap;
- typedef struct BinHeapNode * Position;
- //结点ADT
- struct BinHeapNode {
- int key;
- int degree;
- Position parent;
- Position leftChild;
- Position sibling;
- };
- //用数组内的值建堆
- BinHeap MakeBinHeapWithArray(int keys[], int n);
- //两个堆合并
- BinHeap BinHeapUnion(BinHeap &H1, BinHeap &H2);
- //将H1, H2的根表合并成一个按度数的单调递增次序排列的链表
- BinHeap BinHeapMerge(BinHeap &H1, BinHeap &H2);
- //使H2成为H1的父节点
- void BinLink(BinHeap &H1, BinHeap &H2);
- //返回最小根节点的指针
- BinHeap BinHeapMin(BinHeap heap);
- //减少关键字的值
- void BinHeapDecreaseKey(BinHeap heap, BinHeap x, int key);
- //删除一个关键字
- BinHeap BinHeapDelete(BinHeap &heap, int key);
- //找出一个关键字
- BinHeap BinHeapFind(BinHeap &heap, int key);
- //打印输出堆结构
- void PrintBinHeap(BinHeap heap);
- //销毁堆
- void DestroyBinHeap(BinHeap &heap);
- //用数组内的值建堆
- BinHeap MakeBinHeapWithArray(int keys[], int n) {
- BinHeap heap = NULL, newHeap = NULL;
- for (int i = 0; i < n; i++) {
- newHeap = (BinHeap) malloc(sizeof(BinNode));
- if (newHeap == NULL) {
- puts("Out of the Space");
- exit(1);
- }
- memset(newHeap, 0, sizeof(BinNode));
- newHeap->key = keys[i];
- if (NULL == heap) {
- heap = newHeap;
- } else {
- heap = BinHeapUnion(heap, newHeap);
- newHeap = NULL;
- }
- }
- return heap;
- }
- //两个堆合并
- BinHeap BinHeapUnion(BinHeap &H1, BinHeap &H2) {
- Position heap = NULL, pre_x = NULL, x = NULL, next_x = NULL;
- heap = BinHeapMerge(H1, H2);
- if (heap == NULL) {
- return heap;
- }
- pre_x = NULL;
- x = heap;
- next_x = x->sibling;
- while (next_x != NULL) {
- if ((x->degree != next_x->degree) ||//Cases 1 and 2
- ((next_x->sibling != NULL) && (next_x->degree == next_x->sibling->degree))) {
- pre_x = x;
- x = next_x;
- } else if (x->key <= next_x->key) {//Cases 3
- x->sibling = next_x->sibling;
- BinLink(next_x, x);
- } else {//Cases 4
- if (pre_x == NULL) {
- heap = next_x;
- } else {
- pre_x->sibling = next_x;
- }
- BinLink(x, next_x);
- x = next_x;
- }
- next_x = x->sibling;
- }
- return heap;
- }
- //将H1, H2的根表合并成一个按度数的单调递增次序排列的链表
- BinHeap BinHeapMerge(BinHeap &H1, BinHeap &H2) {
- //heap->堆的首地址,H3为指向新堆根结点
- BinHeap heap = NULL, firstHeap = NULL, secondHeap = NULL,
- pre_H3 = NULL, H3 = NULL;
- if (H1 != NULL && H2 != NULL){
- firstHeap = H1;
- secondHeap = H2;
- //整个while,firstHeap, secondHeap, pre_H3, H3都在往后顺移
- while (firstHeap != NULL && secondHeap != NULL) {
- if (firstHeap->degree <= secondHeap->degree) {
- H3 = firstHeap;
- firstHeap = firstHeap->sibling;
- } else {
- H3 = secondHeap;
- secondHeap = secondHeap->sibling;
- }
- if (pre_H3 == NULL) {
- pre_H3 = H3;
- heap = H3;
- } else {
- pre_H3->sibling = H3;
- pre_H3 = H3;
- }
- if (firstHeap != NULL) {
- H3->sibling = firstHeap;
- } else {
- H3->sibling = secondHeap;
- }
- }//while
- } else if (H1 != NULL) {
- heap = H1;
- } else {
- heap = H2;
- }
- H1 = H2 = NULL;
- return heap;
- }
- //使H2成为H1的父节点
- void BinLink(BinHeap &H1, BinHeap &H2) {
- H1->parent = H2;
- H1->sibling = H2->leftChild;
- H2->leftChild = H1;
- H2->degree++;
- }
- //返回最小根节点的指针
- BinHeap BinHeapMin(BinHeap heap) {
- Position y = NULL, x = heap;
- int min = INT_MAX;
- while (x != NULL) {
- if (x->key < min) {
- min = x->key;
- y = x;
- }
- x = x->sibling;
- }
- return y;
- }
- //抽取有最小关键字的结点
- BinHeap BinHeapExtractMin(BinHeap &heap) {
- BinHeap pre_y = NULL, y = NULL, x = heap;
- int min = INT_MAX;
- while (x != NULL) {
- if (x->key < min) {
- min = x->key;
- pre_y = y;
- y = x;
- }
- x = x->sibling;
- }
- if (y == NULL) {
- return NULL;
- }
- if (pre_y == NULL) {
- heap = heap->sibling;
- } else {
- pre_y->sibling = y->sibling;
- }
- //将y的子结点指针reverse
- BinHeap H2 = NULL, p = NULL;
- x = y->leftChild;
- while (x != NULL) {
- p = x;
- x = x->sibling;
- p->sibling = H2;
- H2 = p;
- p->parent = NULL;
- }
- heap = BinHeapUnion(heap, H2);
- return y;
- }
- //减少关键字的值
- void BinHeapDecreaseKey(BinHeap heap, BinHeap x, int key) {
- if(key > x->key) {
- puts("new key is greaer than current key");
- exit(1); //不为降键
- }
- x->key = key;
- BinHeap z = NULL, y = NULL;
- y = x;
- z = x->parent;
- while(z != NULL && z->key > y->key) {
- swap(z->key, y->key);
- y = z;
- z = y->parent;
- }
- }
- //删除一个关键字
- BinHeap BinHeapDelete(BinHeap &heap, int key) {
- BinHeap x = NULL;
- x = BinHeapFind(heap, key);
- if (x != NULL) {
- BinHeapDecreaseKey(heap, x, INT_MIN);
- return BinHeapExtractMin(heap);
- }
- return x;
- }
- //找出一个关键字
- BinHeap BinHeapFind(BinHeap &heap, int key) {
- Position p = NULL, x = NULL;
- p = heap;
- while (p != NULL) {
- if (p->key == key) {
- return p;
- } else {
- if((x =BinHeapFind(p->leftChild, key)) != NULL) {
- return x;
- }
- p = p->sibling;
- }
- }
- return NULL;
- }
- //打印输出堆结构
- void PrintBinHeap(BinHeap heap) {
- if (NULL == heap) {
- return ;
- }
- Position p = heap;
- while (p != NULL) {
- printf(" (");
- printf("%d", p->key);
- //显示其孩子
- if(NULL != p->leftChild) {
- PrintBinHeap(p->leftChild);
- }
- printf(") ");
- p = p->sibling;
- }
- }
- int kp1[8] = {12,
- 7, 25,
- 15, 28, 33, 41};
- int kp2[20] = {18,
- 3, 37,
- 6, 8, 29, 10, 44, 30, 23, 2, 48, 31, 17, 45, 32, 24, 50, 55};
- int kp4[23] = {37, 41,
- 10, 28, 13, 77,
- 1, 6, 16, 12, 25, 8, 14, 29, 26, 23, 18, 11, 17, 38, 42, 27};
- int main() {
- BinHeap H1 = NULL;
- H1 = MakeBinHeapWithArray(kp1, 7);
- puts("第一个二叉堆H1:");
- PrintBinHeap(H1);
- BinHeap H2 = NULL;
- H2 = MakeBinHeapWithArray(kp2, 19);
- puts("\n\n第二个二叉堆H2:");
- PrintBinHeap(H2);
- BinHeap H3 = NULL;
- H3 = BinHeapUnion(H1, H2);
- puts("\n\n合并H1,H2后,得到H3:");
- PrintBinHeap(H3);
- BinHeap H4 = NULL;
- H4 = MakeBinHeapWithArray(kp4, 22);
- puts("\n\n用于测试提取和删除的二叉堆H4:");
- PrintBinHeap(H4);
- BinHeap extractNode = BinHeapExtractMin(H4);
- if (extractNode != NULL) {
- printf("\n\n抽取最小的值%d后:\n", extractNode->key);
- PrintBinHeap(H4);
- }
- extractNode = BinHeapExtractMin(H4);
- if (extractNode != NULL) {
- printf("\n\n抽取最小的值%d后:\n", extractNode->key);
- PrintBinHeap(H4);
- }
- extractNode = BinHeapExtractMin(H4);
- if (extractNode != NULL) {
- printf("\n\n抽取最小的值%d后:\n", extractNode->key);
- PrintBinHeap(H4);
- }
- BinHeapDelete(H4, 12);
- puts("\n\n删除12后:");
- PrintBinHeap(H4);
- return 0;
- }