#ifndef CA_UWATERLOO_ALUMNI_DWHARDER_BEAP #define CA_UWATERLOO_ALUMNI_DWHARDER_BEAP #include #include // Author: Douglas Wilhelm Harder // Copyright (c) 2009 by Douglas Wilhelm Harder. All rights reserved. // Under construction.... namespace Data_structures { /**************************************************** * ************************************************ * * * Bi-parental Heap * * * ************************************************ * ****************************************************/ /**************************************************** * Bi-parental Heap (Beap) * class Beap * * Author: Douglas Wilhelm Harder * 2009-10-21 * * The concept of a bi-parental heap was first * described by Ian Munro and Hendra Suwanda. * Most operations are O(sqrt(n)) and consequently, * this class has been modified to ensure that * the unused memory is also O(sqrt(n)). * * To Do: implement bool find( Type const & ) const * void erase( Type const & ) ****************************************************/ template class Beap { public: Beap( int = 3 ); ~Beap(); Beap( Beap const & ); Beap &operator=( Beap const & ); bool empty() const; int size() const; int capacity() const; int height() const; Type top() const; Type back() const; bool find( Type const & ) const; int count( Type const & ) const; void push( Type const & ); void erase( Type const & ); void pop(); void clear(); private: int heap_height; int heap_size; int initial_capacity; int heap_capacity; Type *array; void left_push( Type const &, int, int ); void right_push( Type const &, int, int ); void increase_capacity(); void decrease_capacity(); static int first( int ); static int last( int ); template friend std::ostream &operator<<( std::ostream &, const Beap & ); }; /**************************************************** * ************************************************ * * * Bi-parental Heap Definitions * * * ************************************************ * ****************************************************/ /* * Beap Constructor * Beap :: Beap( int h = 3 ) * * The argument is the default height of the allocated heap. * A heap is empty if the heap size is zero. * O(1) */ template Beap::Beap( int h ): heap_height( std::max( 0, h ) ), heap_size( 0 ), initial_capacity( (height() + 1)*(height() + 2)/2 ), heap_capacity( initial_capacity ), array( new Type[capacity()] ) { // Empty constructor } /* * Beap Desstructor * Beap :: ~Beap() * * Delete the memory allocated for the array. * O(1) */ template Beap::~Beap() { delete [] array; } /* * Beap Copy Constructor * Beap :: Beap( int h = 3 ) * * Just use the assignment operator. * O(n) */ template Beap::Beap( Beap const &beap ): heap_height( 0 ), heap_size( 0 ), initial_capacity( 0 ), heap_capacity( 0 ), array( 0 ) { *this = beap; } /* * Assignment Operator * Beap &Beap :: operator=( Beap const & ) * * Just use the assignment operator. * O(n) */ template Beap &Beap::operator=( Beap const &rhs ) { if ( this == &rhs ) { return *this; } if ( capacity() != rhs.capacity() ) { delete [] array; array = new Type[rhs.capacity()]; } for ( int i = 0; i < rhs.size(); ++i ) { array[i] = rhs.array[i]; } heap_height = rhs.heap_height; heap_size = rhs.heap_size; initial_capacity = rhs.initial_capacity; heap_capacity = rhs.heap_capacity; } /* * Heap Empty Query * bool Beap :: empty() const * * A heap is empty if the heap size is zero. * O(1) */ template bool Beap::empty() const { return heap_size == 0; } /* * Heap Size * int Beap :: size() const * * The number of objects is stored in the * member variable 'heap_size'. * O(1) */ template int Beap::size() const { return heap_size; } /* * Heap Capacity * int Beap :: capacity() const * * The size of the array is stored in the * member variable 'heap_capacity'. * O(1) */ template int Beap::capacity() const { return heap_capacity; } /* * Heap Height * int Beap :: height() const * * The height of the heap is stored in the * member variable 'heap_capacity'. * O(1) */ template int Beap::height() const { return heap_height; } /* * Top of the Heap * Type Beap :: top() const * * The object at the top of the heap stored * in array[0]. * O(1) */ template Type Beap::top() const { if ( empty() ) { return Type(); } return array[0]; } /* * Back of the Heap * Type Beap :: back() const * * The object at the back of the heap. * O(sqrt(n)) */ template Type Beap::back() const { if ( empty() ) { return Type(); } Type max = array[size() - height() - 2]; for ( int i = size() - height() - 1; i < size(); ++i ) { if ( array[i] > max ) { max = array[i]; } } return max; } /* * Find an Type in the Heap * bool Beap :: find( Type const &obj ) const * * Determine if the argument is in the heap. * Starting at the bottom left corner, continue moving: * - Up and to the right if obj < what is stored there, and * - Down and to the right if obj > what is stored there. * * In the last case, if we are on the bottom row, we have * to move across or even across and up and to the right. * O(sqrt(n)) */ template bool Beap::find( Type const &obj ) const { if ( empty() ) { return false; } // Starting at the bottom left, // If the object is less than int h = height(); int posn = h*(h + 1)/2; while ( true ) { if ( array[posn] < obj ) { // Move down and to the right // // x // x x // < a x x // x * x x // x x x x x if ( posn == (height() + 1)*(height() + 2)/2 - 1 ) { return false; } if ( posn == height()*(height() + 1)/2 - 1 && size() != (height() + 1)*(height() + 2)/2 ) { return false; } if ( posn + h + 2 < size() ) { // Move down to the right posn += h + 2; ++h; } else { // Move across to the right ++posn; // Move up to the right if ( posn == size() ) { posn -= h; --h; } } } else if ( array[posn] > obj ) { // Move down and to the right // // x // * x // > a x x // x x x x // x x x x x if ( posn == (h + 1)*(h + 2)/2 - 1 ) { return false; } else { posn -= h; --h; } } else { return true; } } } /* * Cont the number of occurances of Type in the Heap * int Beap :: count( Type const &obj ) const * * Determine the number of occurances of the object in the heap. * Starting at the bottom left corner, continue moving: * - Up and to the right if obj < what is stored there, and * - Down and to the right if obj > what is stored there. * * In the last case, if we are on the bottom row, we have * to move across or even across and up and to the right. * O(sqrt(n)) */ template int Beap::count( Type const &obj ) const { if ( empty() ) { return 0; } // Starting at the bottom left, // If the object is less than int object_count = 0; int h = height(); int posn = h*(h + 1)/2; while ( true ) { if ( obj >= array[posn] ) { if ( obj == array[posn] ) { int pt = posn; int ht = h; while ( obj == array[pt] ) { ++object_count; if ( pt == (ht + 1)*(ht + 2)/2 - 1 ) { break; } pt -= ht; --ht; } } if ( posn == (height() + 1)*(height() + 2)/2 - 1 ) { break; } if ( posn == height()*(height() + 1)/2 - 1 && size() != (height() + 1)*(height() + 2)/2 ) { break; } if ( posn + h + 2 < size() ) { // Move down to the right posn += h + 2; ++h; } else { // Move across to the right ++posn; // Move up to the right if ( posn == size() ) { posn -= h; --h; } } } else if ( obj < array[posn] ) { if ( posn == (h + 1)*(h + 2)/2 - 1 ) { break; } else { posn -= h; --h; } } } return object_count; } /* * Heap Push * void Beap :: push( Type const & ) * * Push a new object onto the heap by assuming it * is located in the next available location in the * array and percolating it up the heap by swapping * it with that parent which is both larger than * it and largest. * O(sqrt(n)) */ template void Beap::push( Type const &obj ) { // If the heap is empty, just place the object // in the first location and reutrn. if ( empty() ) { array[0] = obj; heap_size = 1; heap_height = 0; return; } // Update the heap size and, if necessary, // the heap height and capacity. int posn = heap_size; if ( posn == first( height() + 1 ) ) { ++heap_height; if ( size() == capacity() ) { increase_capacity(); } } ++heap_size; // Initially assume that the new object goes into // the next available location in the array // and move up the heap determining which is the // largest between the newly-inserted object // and its two parents. for ( int h = heap_height; h >= 2; --h ) { if ( posn == first( h ) ) { return left_push( obj, posn, h ); } else if ( posn == last( h ) ) { return right_push( obj, posn, h ); } else { // Percolate up the centre // // x // x x // x x x // x * * x // x x * x x // // Swap with the largest parent if larger; // otherwise, store the object and return. int left_parent = posn - h - 1; int right_parent = posn - h; if ( obj < array[left_parent] && array[left_parent] > array[right_parent] ) { array[posn] = array[left_parent]; posn = left_parent; } else if ( obj < array[right_parent] ) { array[posn] = array[right_parent]; posn = right_parent; } else { array[posn] = obj; return; } } } if ( obj < array[0] ) { array[posn] = array[0]; array[0] = obj; } else { array[posn] = obj; } } /* * Percolate up the left edge of the heap * void Beap :: push_left( obj, posn, height ) * * Percolate up the left edge * * * * * x * * x x * * x x x * * x x x x <- height * * Swap if the parent is greater; * otherwise, store the object and return. * O(sqrt(n)) */ template void Beap::left_push( Type const &obj, int posn, int height ) { assert( posn == first( height ) ); for ( int h = height; h >= 1; --h ) { int parent = posn - h; if ( obj < array[parent] ) { array[posn] = array[parent]; posn = parent; } else { array[posn] = obj; return; } } array[posn] = obj; } /* * Percolate up the right edge of the heap * void Beap :: push_right( obj, posn, height ) * * Percolate up the right edge * * * * x * * x x * * x x x * * x x x x * <- height * * Swap if the parent is greater; * otherwise, store the object and return. * O(sqrt(n)) */ template void Beap::right_push( Type const &obj, int posn, int height ) { assert( posn == last( height ) ); for ( int h = height; h >= 1; --h ) { int parent = posn - h - 1; if ( obj < array[parent] ) { array[posn] = array[parent]; posn = parent; } else { array[posn] = obj; return; } } array[posn] = obj; } template void Beap::pop() { // If empty, simply return. if ( empty() ) { return; } // If the heap size is one, just reset the appropriate // member variables. if ( size() == 1 ) { heap_size = 0; heap_height = -1; return; } // Shrink the height of the heap if necessary. --heap_size; if ( heap_size == first( height() ) ) { --heap_height; if ( (height() + 3)*(height() + 4)/2 == capacity() && capacity() > initial_capacity ) { decrease_capacity(); } } // Assume the last object (array[posn]) fits in the // root and percolate down until it fits. int posn = 0; for ( int h = 0; h < height() - 1; ++h ) { int left = posn + h + 1; int right = posn + h + 2; if ( array[left] < array[right] && array[left] < array[size()] ) { array[posn] = array[left]; posn = left; } else if ( array[right] < array[size()] ) { array[posn] = array[right]; posn = right; } else { array[posn] = array[size()]; return; } } // We must be careful with the last row, as there are // three possible cases: // Both children are in the heap, // Only the left child is in the heap, or // Neither child is in the heap. int left = posn + height(); int right = left + 1; if ( right < size() ) { if ( array[right] < array[left] && array[right] < array[size()] ) { array[posn] = array[right]; array[right] = array[size()]; return; } } if ( left < size() ) { if ( array[left] < array[size()] ) { array[posn] = array[left]; array[left] = array[size()]; return; } } array[posn] = array[size()]; } /* * Clear the Beap * void Beap :: clear() * * Add another row to the heap and copy all objects over. * O(1) */ template void Beap::clear() { heap_size = 0; heap_height = -1; } /**************************************************** * ************************************************ * * * Private Bi-parental Heap Definitions * * * ************************************************ * ****************************************************/ /* * Increase the Heap Capacity * void Beap :: increase_capacity() * * Add another row to the heap and copy all objects over. * The maximum unused space is O(sqrt(n)), and * the amortized copies per push is O(sqrt(n)). * As insertion is already O(sqrt(n)), this makes no difference. * O(n) */ template void Beap::increase_capacity() { int new_capacity = (height() + 2)*(height() + 3)/2; Type *new_array = new Type[new_capacity]; for ( int i = 0; i < size(); ++i ) { new_array[i] = array[i]; } delete [] array; heap_capacity = new_capacity; array = new_array; } /* * Decrease the Heap Capacity * void Beap :: decrease_capacity() * * Remove a row from the heap and copy all objects over. * The maximum unused space is O(sqrt(n)), and * the amortized copies per pop is O(sqrt(n)). * As pop is already O(sqrt(n)), this makes no difference. */ template void Beap::decrease_capacity() { int new_capacity = (height() + 2)*(height() + 3)/2; Type *new_array = new Type[new_capacity]; for ( int i = 0; i <= size(); ++i ) { new_array[i] = array[i]; } delete [] array; heap_capacity = new_capacity; array = new_array; } /* * First Entry of a Row of the Heap * static int Beap :: first( int h ) * * Returns the location that the first entry of the * array for height h. */ template int Beap::first( int h ) { return h*(h + 1)/2; } /* * Last Entry of a Row of the Heap * static int Beap :: last( int h ) * * Returns the location that the last entry of the * array for height h. */ template int Beap::last( int h ) { return (h + 1)*(h + 2)/2 - 1; } /**************************************************** * ************************************************ * * * Friends * * * ************************************************ * ****************************************************/ /********************************************************************* * out << beap; * * Print the bi-parental heap in the following format: * * Size: 15 * Capacity: 21 * Height: 4 * Empty: 0 * Top: 0 * * 0 * 3 1 * 8 5 2 * 10 9 7 4 * 13 11 14 12 6 * *********************************************************************/ template std::ostream &operator<<( std::ostream &out, const Beap &beap ) { out << "Size: " << beap.size() << std::endl; out << "Capacity: " << beap.capacity() << std::endl; out << "Height: " << beap.height() << std::endl; out << "Empty: " << beap.empty() << std::endl; out << "Top: " << beap.top() << std::endl << std::endl; if ( beap.empty() ) { return out; } int i = 0; for ( int j = 1; true; ++j ) { for ( int k = 0; k < j; ++k ) { out << '\t' << beap.array[i]; ++i; if ( i == beap.size() ) { out << std::endl; return out; } } out << std::endl; } } } #endif