// accelerated binomial heaps, -bds 03Oct, Reference: CLR chapter 19 #ifndef __BINOMIALHEAP_H #define __BINOMIALHEAP_H /* * This implementation DOES provide constructors and the CLR functions: * Binomial_Heap_Insert(), Binomial_Heap_Minimum(), Binomial_Link(), * Binomial_Heap_Extract_Min(), and Binomial_Heap_Union(). * * In this `accelerated' implementation the above functions all run in * O(1) time except Extract_Min() whose amortized worst case time is O(lg(n)), * where n is the current size, n, of the heap. This version of binomial heaps * may be viewed as a step towards Fibnonacci heaps. * * * This implementation DOES NOT provide: * Binomial_Heap_Decrease_Key() or Binomial_Heap_Delete() * * An accelerated binomial heap is just an slist of binomial trees, * with a pointer to the tree whose root holds the minimum key and a * record of the number of keys in the heap. * For amortized analysis, define the potential, p(H), of a heap to be * (c1 + c2) times the number of roots in the tree list, where c1 and c2 * are constant upper bounds bounds on certain portions of the _Extract_Min() * function. The potential of an empty heap is 0. */ #include "binomialTree.h" // defines tree, aka binomialTree< Key >, and it's functions. #include // for use by _Extract_Min() // abbreviations: #define heap binomialHeap< Key > template< class Key > class binomialHeap { private: treeList _trees; treePtr _min; int _n; // the total number of nodes (and keys) in all the trees of the heap. public: // basic set/getters binomialHeap() : _n(0) {} // construct an empty heap by default. friend treeList& trees( heap& H ){ return H._trees; } friend const treeList& trees( const heap& H ){ return H._trees; } friend treePtr& min( heap& H ){ return H._min; } friend const treePtr& min( const heap& H ){ return H._min; } friend int& n( heap& H ){ return H._n; } friend const int& n( const heap& H ){ return H._n; } }; template inline Key Binomial_Heap_Minimum( const heap& H ) /* Return the minimum key from H. H must be nonempty. * Amortized worst case time is the actual time, Theta(1). */ { return key( *min(H) ); } template inline void Binomial_Heap_Insert( heap& H, Key k ) /* Add a one-key tree to H. Amortized worst case time is the * actual time + c1 + c2 (because of the new root), which is Theta(1). */ { tree T; initialize( T, k ); /// add the new tree to H. trees(H).push_front( T ); /// fix min if necessary if ( ( n(H) == 0 ) || ( k < key( *min(H) ) ) ) min(H) = trees(H).begin(); /// fix n n(H)++; } template void Binomial_Heap_Union( heap& A, heap& B ) /* A becomes ( A U B ). Amortized worst case time is the actual time which is * Theta(1). The potential of B is transferred to A, and is still c1 + c2 times * the number of roots. */ { /// Add B's trees to A's. trees(A).splice(trees(A).begin(), trees(B) ); /// which min points to the tree with smaller key at it's root? if ( key( *min(B) ) < key( *min(A) ) ) min(A) = min(B); /// fix n. n(A) += n(B); } template inline void Binomial_Heap_Extract_Min( heap& H ) /* Remove the minimum key (and restore heap properties). H must be nonempty. * Amortized worst case time is O(lg(n)), for n the number of keys in H. * * All steps are Theta(1) except where noted otherwise. */ { int D = static_cast ( 1 + ceil( log( static_cast( n(H) )) /log( 2.0 ))); /* D is 1 + ceil (lg(n)), for n the current number of keys. * No tree of degree greater than D exists at beginning, during, or at * finish of the Extracti_Min process. * * First, we add to H those trees which are children of the root we then * remove. The potential function increases by at most 2D because of the * new roots. * * Let s denote the number of roots H will have after removing min and * adding it's children to the root list. * Thus s = trees(H).size() - 1 + children(*min(H)).size(). */ trees(H).splice(trees(H).begin(), children( *min(H) ) ); trees(H).erase( min(H) ); n(H)--; /* Now s == trees(H).size(). * This step took amortized worst case time = actual constant time plus * (c1 + c2) times O(D), where the O(D) is for the children of the min node * which became roots. The potential function is now at p(H) = (c1 + c2)*s. * * dear H, a problem arises: do you know where your min is? * We'll compactify things and find the min at the same time. * H may have many trees of the same degree (s may be much bigger than D). * For each degree we'll link them up as we find them. */ /// Next, Create a place to accumulate the combined trees from H treePtr nil; typedef vector< treePtr > array; L0: array A( D, nil ); // place to accumulate the combined trees // Worst case time to initialize A is Theta(D). /// Accumulate the nodes, linking as we find same degree nodes. L1: for ( treePtr p = trees(H).begin(); p != trees(H).end(); ++p ) // Amortized cost O(D) to link up any equal degree trees. { tree& T = *p; int d; for ( d = degree(T); A[d] != nil; d = degree(T) ) { T = Binomial_Link( T, *A[d] ); A[d] = nil; } A[ d ] = p; } /* In the above loop each of s nodes is put in A. Some are also linked into * others. let c1 be an upper bound for this putting and linking. Note * that the body of the inner for loop is executed at most s times because * each node is linked under another most once. Those nodes which were * linked as children of others will not be roots in the final result, so * the potential decreases by at most c1*(s - O(D)), where O(D) trees are * directly referenced from A. * * One unit of potential from all but O(D) roots is used up: * p(H) -= c1*(s - O(D)). Now p(H) = c1*(trees in A) + c2*s. */ /// Now put the good nodes in a treeList to go into the revised heap. treeList L; L2: for (typename array::iterator p = A.begin(); p != A.end(); ++p) // Worst case time is Theta(D). { treePtr x = *p; if ( x != nil ) L.push_front( *x ); // impt that this {/em copies} something (from H) into L. } L3: trees(H).clear(); /* amortized O(D). Let c2 be the cost of deallocating a list node from * memory. The second unit of potential from s - O(D) roots is used up: * p(H) -= c2*(s - O(D)). Now p(H) = (c1 + c2)*(trees in L). */ trees(H).swap(L); // Theta(1), as Zhendong points out. // now p(H) = (c1 + c2)*(trees(H).size()) as desired. /// now finish by finding the min of the short list. L4: min( H ) = min_element( trees(H).begin(), trees(H).end(), lessByKey() ); // Worst case time is O(D). } #endif #if 0 /* test code. * To use it, copy file to .cc and change #if value above to 1. * To restore as a header, copy file to .h and change #if value above to 0. */ #include using namespace std; int main() { binomialHeap< long > H; //slist< binomialTree< long > > H; // binomialHeap< long > // Binomial_Heap_Insert( H, long(99) ); // cout << Binomial_Heap_Minimum( H ) << " first min " << endl; for (long i = 0; i < 256; ++i) Binomial_Heap_Insert( H, i+100 ); for (int i = 0; i < 100; ++i) { cout << Binomial_Heap_Minimum( H ) << " "; Binomial_Heap_Extract_Min( H ); Binomial_Heap_Extract_Min( H ); Binomial_Heap_Insert( H, random()%200 ); } cout << endl; } #endif