#ifndef hash_set_h_ #define hash_set_h_ // File: hash_set.h // Purpose: Implement the basic function of the set class as a hash // table instead of a binary search tree. In the usage of the // class, the primary difference between versions is that the // iterators do not step through the hash set in any intuitively // meaningful order --- it is just the order imposed by the hash // function. // // Details of the class are below and in the associated lecture notes. #include #include #include #include // The hash_set is templated over both the type of key and the type // of the hash function, a function object. template < typename KeyType, typename HashFunc > class hash_set { private: std::vector< std::list > m_table; // actual table HashFunc m_hash; // hash function unsigned int m_size; // number of keys typedef typename std::list::iterator hash_list_itr; public: // Start with the iterator class. This is defined as a nested // class so that it does not have to be templated separately over // the hash function object type. class iterator { public: friend class hash_set; // allows access to private variables private: hash_set* m_hs; // pointer to the hash_set object int m_index; // current index in the hash table hash_list_itr m_list_itr; // current iterator at the current index private: // private constructors for use by the hash_set only iterator( hash_set * hs ) : m_hs(hs), m_index(-1) {} iterator( hash_set* hs, int index, hash_list_itr loc ) : m_hs(hs), m_index(index), m_list_itr(loc) {} public: // Two ordinary constructors iterator() : m_hs(0), m_index(-1) {} iterator( iterator const& itr ) : m_hs(itr.m_hs), m_index(itr.m_index), m_list_itr(itr.m_list_itr) {} iterator& operator=( const iterator& old ) { m_hs = old.m_hs; m_index = old.m_index; m_list_itr = old.m_list_itr; return *this; } // The dereference operator need only worry about the current // list iterator, and does not need to check the current index. const KeyType& operator*() const { return *m_list_itr; } // The comparison operators must account for the list iterators // being unassigned at the end. friend bool operator== ( const iterator& lft, const iterator& rgt ) { return lft.m_hs == rgt.m_hs && lft.m_index == rgt.m_index && ( lft.m_index == -1 || lft.m_list_itr == rgt.m_list_itr); } friend bool operator!= ( const iterator& lft, const iterator& rgt ) { return lft.m_hs != rgt.m_hs || lft.m_index != rgt.m_index || ( lft.m_index != -1 && lft.m_list_itr != rgt.m_list_itr); } // pre-increment iterator& operator++( ) { this->next(); return *this; } // post-increment is indicated by the dummy int argument iterator operator++( int ) { iterator temp( *this ); this->next(); return temp; } // pre-decrement iterator & operator--( ) { this->prev(); return *this; } // post-decrement is indicated by the dummy int argument iterator operator--( int ) { iterator temp( *this ); this->prev(); return temp; } private: // Find the next entry in the table void next() { ++ m_list_itr; // next item in the list // If we are at the end of this list if ( m_list_itr == m_hs->m_table[m_index].end() ) { // Find the next non-empty list in the table for ( ++m_index; m_index < int(m_hs->m_table.size()) && m_hs->m_table[m_index].empty(); ++m_index ) ; // If one is found, assign the m_list_itr to the start if ( m_index != int(m_hs->m_table.size()) ) m_list_itr = m_hs->m_table[m_index].begin(); // Otherwise, we are at the end else m_index = -1; } } // Find the previous entry in the table void prev() { // If we aren't at the start of the current list, just // decrement the list iterator if ( m_list_itr != m_hs->m_table[m_index].begin() ) m_list_itr -- ; else { // Otherwise, back down the table until the previous non-empty // list in the table is found for ( --m_index; m_index >= 0 && m_hs->m_table[m_index].empty(); --m_index ) ; // Go to the last entry in the list. m_list_itr = m_hs->m_table[m_index].begin(); hash_list_itr p = m_list_itr; ++p; for ( ; p != m_hs->m_table[m_index].end(); ++p, ++m_list_itr ) ; } } }; ///////////////////////////////// // On to the actual hash set // ///////////////////////////////// // Constructor for the table set just accepts the size of the // table. The default constructor for the hash function object is // implicitly used. hash_set( unsigned int init_size = 10 ) : m_table(init_size), m_size(0) {} // Copy constructor just uses the member function copy // constructors. hash_set( const hash_set& old ) : m_table(old.m_table), m_size(old.m_size) {} ~hash_set() {} hash_set& operator=( const hash_set& old ) { if ( &old != this ) *this = old; } unsigned int size() const { return m_size; } // Insert the key if it is not already there. std::pair< iterator, bool > insert( KeyType const& key ) { // const float LOAD_FRACTION_FOR_RESIZE = 1.25; } // Find the key, using hash function computation, indexing and list // find. iterator find( const KeyType& key ) { unsigned int hash_value = m_hash(key); unsigned int index = hash_value % m_table.size(); hash_list_itr p = std::find( m_table[index].begin(), m_table[index].end(), key ); if ( p == m_table[index].end() ) return this->end(); else return iterator( this, index, p ); } // erase the key int erase( const KeyType& key ) { // Find the key and use the erase iterator function. iterator p = find( key ); if ( p == end() ) return 0; else { erase(p); return 1; } } // erase at the iterator void erase( iterator p ) { m_table[ p.m_index ].erase( p.m_list_itr ); } // Find the first entry in the table and create an associated // iterator. iterator begin() { // Implemented in class } // Create an end iterator. iterator end() { iterator p( this ); p.m_index = -1; return p; } // A public print utility. void print( std::ostream & ostr ) { for ( unsigned int i=0; i