/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef _DECAF_UTIL_PRIORITYQUEUE_H_ #define _DECAF_UTIL_PRIORITYQUEUE_H_ #include #include #include #include #include #include #include #include #include #include #include namespace decaf { namespace util { class DECAF_API PriorityQueueBase { protected: static const int DEFAULT_CAPACITY; static const int DEFAULT_CAPACITY_RATIO; virtual ~PriorityQueueBase() {} }; /** * An unbounded priority queue based on a binary heap algorithm. The elements of the priority queue * are ordered according to their natural ordering, or by a Comparator provided to one of the constructors * that accepts Comparators. A priority queue relying on natural ordering also does not permit insertion * of non-comparable objects (doing so may result in a compiler error). * * The head of this queue is the least element with respect to the specified ordering. If multiple * elements are tied for least value, the head is one of those elements -- ties are broken arbitrarily. * The queue retrieval operations poll, remove, peek, and element access the element at the head of * the queue. * * A priority queue is unbounded, but has an internal capacity governing the size of an array used to store * the elements on the queue. It is always at least as large as the queue size. As elements are added to * a priority queue, its capacity grows automatically. The details of the growth policy are not specified. * * This class and its iterator implement all of the optional methods of the Collection and Iterator * interfaces. The Iterator provided in method iterator() is not guaranteed to traverse the elements of * the priority queue in any particular order. If you need ordered traversal, consider using * Arrays::sort( pq.toArray() ). * * Note that this implementation is not synchronized. Multiple threads should not access a PriorityQueue * instance concurrently if any of the threads modifies the queue. Instead, use the thread-safe * PriorityBlockingQueue class. * * Implementation note: this implementation provides O(log(n)) time for the enqueing and dequeing methods * (offer, poll, remove() and add); linear time for the remove(Object) and contains(Object) methods; * and constant time for the retrieval methods (peek, element, and size). * * @since 1.0 */ template< typename E > class PriorityQueue : public AbstractQueue, private PriorityQueueBase { private: int _size; int capacity; E* elements; decaf::lang::Pointer< Comparator > _comparator; private: class PriorityQueueIterator : public Iterator { private: PriorityQueueIterator( const PriorityQueueIterator& ); PriorityQueueIterator& operator= ( const PriorityQueueIterator& ); private: int position; bool allowRemove; PriorityQueue* queue; public: PriorityQueueIterator( PriorityQueue* queue ) : position( 0 ), allowRemove( false ), queue( queue ) {} virtual E next() { if( !hasNext() ) { throw NoSuchElementException( __FILE__, __LINE__, "No more elements to Iterate over." ); } allowRemove = true; return queue->elements[position++]; } virtual bool hasNext() const { return position < queue->_size; } virtual void remove() { if( !allowRemove ) { throw lang::exceptions::IllegalStateException( __FILE__, __LINE__, "No more elements to Iterate over." ); } allowRemove = false; queue->removeAt( position-- ); } }; class ConstPriorityQueueIterator : public PriorityQueueIterator { private: ConstPriorityQueueIterator( const ConstPriorityQueueIterator& ); ConstPriorityQueueIterator& operator= ( const ConstPriorityQueueIterator& ); public: ConstPriorityQueueIterator( const PriorityQueue* queue ) : PriorityQueueIterator( const_cast( queue ) ) {} virtual void remove() { throw lang::exceptions::UnsupportedOperationException( __FILE__, __LINE__, "PriorityQueue::Iterator::remove - Not Valid on a Const Iterator" ); } }; friend class PriorityQueueIterator; public: /** * Creates a Priority Queue with the default initial capacity. */ PriorityQueue() : AbstractQueue(), _size( 0 ), capacity( 0 ), elements( NULL ), _comparator() { this->initQueue( DEFAULT_CAPACITY, new comparators::Less() ); } /** * Creates a Priority Queue with the capacity value supplied. * * @param initialCapacity * The initial number of elements allocated to this PriorityQueue. */ PriorityQueue( int initialCapacity ) : AbstractQueue(), _size( 0 ), capacity( 0 ), elements( NULL ), _comparator() { this->initQueue( initialCapacity, new comparators::Less() ); } /** * Creates a Priority Queue with the default initial capacity. This new PriorityQueue takes * ownership of the passed Comparator instance and uses that to determine the ordering of the * elements in the Queue. * * @param initialCapacity * The initial number of elements allocated to this PriorityQueue. * @param comparator * The Comparator instance to use in sorting the elements in the Queue. * * @throws NullPointerException if the passed Comparator is NULL. */ PriorityQueue( int initialCapacity, Comparator* comparator ) : AbstractQueue(), _size( 0 ), capacity( 0 ), elements( NULL ), _comparator() { if( comparator == NULL ) { throw decaf::lang::exceptions::NullPointerException( __FILE__, __LINE__, "Passed Comparator Cannot be NULL." ); } this->initQueue( initialCapacity, comparator ); } /** * Creates a PriorityQueue containing the elements in the specified Collection. * * @param source * the Collection whose elements are to be placed into this priority queue */ PriorityQueue( const Collection& source ) : AbstractQueue(), _size( 0 ), capacity( 0 ), elements( NULL ), _comparator() { this->getFromCollection( source ); } /** * Creates a PriorityQueue containing the elements in the specified priority queue. This priority * queue will be ordered according to the same ordering as the given priority queue. * * @param source * the priority queue whose elements are to be placed into this priority queue */ PriorityQueue( const PriorityQueue& source ) : AbstractQueue(), _size( 0 ), capacity( 0 ), elements( NULL ), _comparator() { this->getFromPriorityQueue( source ); } virtual ~PriorityQueue() { delete [] elements; } /** * Assignment operator, assign another Collection to this one. * * @param source * The Collection to copy to this one. */ PriorityQueue& operator= ( const Collection& source ) { this->getFromCollection( source ); return *this; } /** * Assignment operator, assign another PriorityQueue to this one. * * @param source * The PriorityQueue to copy to this one. */ PriorityQueue& operator= ( const PriorityQueue& source ) { this->getFromPriorityQueue( source ); return *this; } public: virtual decaf::util::Iterator* iterator() { return new PriorityQueueIterator( this ); } virtual decaf::util::Iterator* iterator() const { return new ConstPriorityQueueIterator( this ); } virtual int size() const { return this->_size; } virtual void clear() { // TODO - Provide a more efficient way to clear the array without reallocating it // we should keep the size it grew to since if reused it could get that big // again and reallocating all that memory could be to slow. delete [] this->elements; this->elements = new E[DEFAULT_CAPACITY]; this->capacity = DEFAULT_CAPACITY; this->_size = 0; } virtual bool offer( const E& value ) { // TODO - Check for Null and throw exception increaseCapacity( _size + 1 ); elements[_size++] = value; upHeap(); return true; } virtual bool poll( E& result ) { if( this->isEmpty() ) { return false; } result = elements[0]; removeAt( 0 ); return true; } virtual bool peek( E& result ) const { if( this->isEmpty() ) { return false; } result = elements[0]; return true; } virtual E remove() { if( !this->isEmpty() ) { E result = elements[0]; removeAt( 0 ); return result; } throw decaf::util::NoSuchElementException( __FILE__, __LINE__, "Unable to remove specified element from the Queue." ); } virtual bool remove( const E& value ) { int targetIndex = 0; for( targetIndex = 0; targetIndex < _size; targetIndex++ ) { if( 0 == _comparator->compare( value, elements[targetIndex] ) ) { break; } } if( _size == 0 || _size == targetIndex ) { return false; } removeAt( targetIndex ); return true; } virtual bool add( const E& value ) { try { return offer( value ); } DECAF_CATCH_RETHROW( lang::exceptions::UnsupportedOperationException ) DECAF_CATCH_RETHROW( lang::exceptions::IllegalArgumentException ) DECAF_CATCH_RETHROW( lang::exceptions::IllegalStateException ) DECAF_CATCH_EXCEPTION_CONVERT( lang::exceptions::NullPointerException, lang::exceptions::UnsupportedOperationException ) DECAF_CATCHALL_THROW( lang::exceptions::UnsupportedOperationException ) } /** * obtains a Copy of the Pointer instance that this PriorityQueue is using to compare the * elements in the queue with. The returned value is a copy, the caller cannot change the * value if the internal Pointer value. * * @return a copy of the Comparator Pointer being used by this Queue. */ decaf::lang::Pointer< Comparator > comparator() const { return this->_comparator; } private: void initQueue( int initialSize, Comparator* comparator ) { this->elements = new E[initialSize]; this->capacity = initialSize; this->_size = 0; this->_comparator.reset( comparator ); } void upHeap() { int current = _size - 1; int parent = ( current - 1 ) / 2; while( current != 0 && _comparator->compare( elements[current], elements[parent] ) < 0 ) { // swap the two E tmp = elements[current]; elements[current] = elements[parent]; elements[parent] = tmp; // update parent and current positions. current = parent; parent = ( current - 1 ) / 2; } } void downHeap( int pos ) { int current = pos; int child = 2 * current + 1; while( child < _size && !this->isEmpty() ) { // compare the children if they exist if( child + 1 < _size && _comparator->compare( elements[child + 1], elements[child] ) < 0 ) { child++; } // compare selected child with parent if( _comparator->compare( elements[current], elements[child] ) < 0 ) { break; } // swap the two E tmp = elements[current]; elements[current] = elements[child]; elements[child] = tmp; // update child and current positions current = child; child = 2 * current + 1; } } void getFromPriorityQueue( const PriorityQueue& c ) { initCapacity( c ); _comparator = c.comparator(); for( int ix = 0; ix < c.size(); ++ix ) { this->elements[ix] = c.elements[ix]; } _size = c.size(); } void getFromCollection( const Collection& c ) { initCapacity( c ); _comparator.reset( new comparators::Less() ); std::auto_ptr< Iterator > iter( c.iterator() ); while( iter->hasNext() ) { this->offer( iter->next() ); } } void removeAt( int index ) { _size--; elements[index] = elements[_size]; downHeap(index); elements[_size] = E(); } void initCapacity( const Collection& c ) { delete [] elements; _size = 0; if( c.isEmpty() ) { capacity = 1; elements = new E[capacity]; } else { capacity = (int) lang::Math::ceil( (double)c.size() * 1.1 ); elements = new E[capacity]; } } void increaseCapacity( int size ) { if( size > capacity ) { E* newElements = new E[ size * DEFAULT_CAPACITY_RATIO ]; for( int ix = 0; ix < capacity; ix++ ) { newElements[ix] = elements[ix]; } delete [] elements; elements = newElements; capacity = size * DEFAULT_CAPACITY_RATIO; } } }; }} #endif /* _DECAF_UTIL_PRIORITYQUEUE_H_ */