本篇文章轉載於 Binomial Heap - HWCHIU'S BLOGnode
Binomial Heap是由一羣 Binomail Tree所組成的
Binomial Tree(BT)含有下列特性:post
高度為k的 BT共有2^k個nodespa
高度為k的 BT共有2^k個nodecode
是 mergable heap排序
由一羣 Binomial Tree組成,每個BT都滿足 min-heap的性質ip
對於高度為k的BT只能存在最多一棵rem
以二進位來看待的話,第K位就表明是否存在高度為K的BTget
如下圖為例,就是11001 (右邊最小)it
所以任何數量的結點均可以用不一樣的BT給組合出來io
採用Left-Child Right-sibling的方式來實現,左邊指向child,右邊指向同輩
value: node的值
degree: 以此node為root的BT的高度
parent: 指向其parent
class Node{ public: Node parent; Node child; Node* sibling; int value; int degree; Node(){ parent = NULL; child = NULL; sibling = NULL; value = 0; degree = 0; } };
getMin
size
Travese (postorder)
mergeHeap
Insert
deleteMin
由於每個BT自己都已經是min-heap的特性了,所以只要針對每個BT的root比較其值便可
int getMin(){ Node* x = head; int min = INT_MAX; while(x!=NULL){ if(x->value < min) min = x->value; x = x->sibling; } return min; }
由於 Binomial Heap內都是由 Binomial Tree組成,因此能夠由每個BT的degree獲得其node數量
再把全部加總便可。
int size(){ Node* tmp = head; int count=0; while(tmp){ count+= (1<<tmp->degree); // 2^degree tmp = tmp->sibling; } return count; }
這邊是每個BT都要獨立跑一次Postorder的結果,因此在遞迴的過程中要對root作一些控制
//對每一棵BT都跑一次postorder void postorder(){ Node* tmp = head; while(tmp){ _postorder(tmp); tmp = tmp->sibling; } printf("\n"); } //用parent判斷是否是root,避免root跑去呼叫到別的BT void _postorder(Node* node){ if(!node) return; _postorder(node->child); if(node->parent) _postorder(node->sibling); printf("%d ",node->value); }
要合併兩個 Binomial Heap
先把兩個 Binomail Heap的 BT list給從新串接起來,以degree為key作sorting.
再根據這個新的BT list開始進行一系列的合併
若是隻有兩個高度相同的BT,就直接合併
若是有三個高度相同的BT,就把後面兩棵合併(維持sorting)
void MergeHeap(BinomialHeap &bh){ mergeHeap(bh); //先把BT list給從新串接起來 Node* prev = NULL; Node* x = head; Node* next = x->sibling; while(next){ if( (x->degree != next->degree) || next->sibling && next->sibling->degree == x->degree){ prev = x; //前後兩棵BT的高度不一樣 或是 後面三棵BT的高度都相同 x = next; //那就把指標往前移動,下次再合併 } else if( x->value <= next->value){ //前面BT的值比較小,因此後面的合併進來 x->sibling = next->sibling; mergeTree(next,x); } else{ //前面那棵BT的值比較大,要往後合併,視情況也要更新 head指標 if(!prev){ head = next; //更新head 指標 } else{ prev->sibling = next; } mergeTree(x,next); //合併 x = next; } next = next->sibling; } }
要把兩個 Binomial Heap的BT list給從新串接起來,採用merge sort的方法
使用 newHead紀錄合併後的頭
使用 newCurr來紀錄每次合併後的尾
void mergeHeap(BinomialHeap &bh){ Node* head2 = bh.head; Node* head1 = head; Node* newHead, *newCurr; if(!head1){ //若是自己是空的,就不須要合併,直接指向對方便可 head = head2; return ; } else if(!head2){ //對方是空的,也不須要合併 return ; } //先行尋找誰的開頭比較小,當作新串列的頭 if(head1->degree > head2->degree){ newHead = newCurr = head2; head2 = head2->sibling; } else { newHead = newCurr = head1; head1 = head1->sibling; } while(head1 && head2){ if(head1->degree < head2->degree){ newCurr->sibling = head1; newCurr = head1; head1 = head1->sibling; } else { newCurr->sibling = head2; newCurr = head2; head2 = head2->sibling; } } while(head1){ newCurr->sibling = head1; newCurr = head1; head1 = head1->sibling; } while(head2){ newCurr->sibling = head2; newCurr = head2; head2 = head2->sibling; } head = newHead; }
合併兩個 Binomial Tree,由於我們是min-heap的特性,因此當兩棵高度相等的BT要合併時,根據root的值來決定誰是合併後的root.
假設已經知道BT(y)的值比BT(z)還要大,因此BT(z)會是合併後的root
y的parent指到z
y的sibling 指到 z本來的child
z的child 指到y
z的degree 加一
void mergeTree(Node* y,Node* z){ y->parent = z; y->sibling = z->child; z->child = y; z->degree++; }
要插入一個新的元素,就是創見一個新的 Binomial Heap,然後跟本來的Heap執行合併便可
void insert(int value){ BinomialHeap bh; bh.head = new Node(); bh.head->value = value; MergeHeap(bh); }
要從 BinomialHeap中刪除當前最小元素
先找到最小元素所在的那棵BT
把該BT從list裡面拔除
把該BT的children給反向排序(degree為key)
在跟本來的BT list合併
void deleteMin(){ int min = head->value; Node* tmp = head; Node* minPre = NULL; Node* minCurr = head; // 找到最小的node位於何處,由於要將該BT給拔除,因此必須要記得該BT前面那棵BT // 若是最小棵的是第一棵,那也要一併更新 head 指標 while(tmp->sibling){ if(tmp->sibling->value < min){ min = tmp->sibling->value; minPre = tmp; minCurr = tmp->sibling; } tmp = tmp->sibling; } if(!minPre && minCurr) //最小棵是第一個 head = minCurr->sibling; else if(minPre && minCurr) minPre->sibling = minCurr->sibling; //H' Make-BINOMIAL-HEAP() Node *pre,*curr; //用三個指標反轉一個 single link list pre = tmp = NULL; curr = minCurr->child; while(curr){ tmp = curr->sibling; curr->sibling = pre; curr->parent = NULL; pre = curr; curr = tmp; } //創建一棵新的binomial heap,並且讓他的head 指向反轉後的BT list BinomialHeap bh ; bh.head = pre; //再度合併 MergeHeap(bh); }
圖片來自
Introduction To Algorithms,Chapter 19 Binomial Heaps