#ifndef _HASH_H #define _HASH_H //Hash.h // A HashTable f holds a set of(key, value) associations, just like an AssocList, // except all operations are O(1) (provided the hashing is good) /* public interface: //destructor ~HashTable(); // constructors HashTable(); HashTable(VALUE const & def); HashTable(HashTable const & a); // operator= by deep copy HashTable& operator=(HashTable const & a); void clear(); int exists(KEY const & k) const; void remove(KEY const & k); VALUE & operator[](KEY const & k); VALUE operator[](KEY const & k) const; Array keys() const; Array values() const; void adopt(KEY const& k, HashTable& a); // output friend std::ostream & operator<< <>(std::ostream &, const HashTable &); */ #include #include "Array.h" #include "AssocList.h" #include "test_utilities.h" template class HashTable { unsigned int n_keys; unsigned int hash_modulus; Array > buckets; VALUE *default_init; void init(VALUE* def = NULL) { n_keys = 0; hash_modulus = 7; default_init = def; } int hash(KEY const & k, int mod = 0) const { return Hash(k) % (mod ? mod : hash_modulus); } void grow() { //std::cerr << "-------grow " << hash_modulus << std::endl; //std::cerr << *this << std::endl; //std::cerr << "-------" << std::endl; const int old_modulus = hash_modulus; const int new_modulus = hash_modulus*3; Array keys_ = keys(); for (int k = 0; k < keys_.size(); ++k) { const KEY& key = keys_[k]; const int i = hash(key, old_modulus); const int j = hash(key, new_modulus); buckets[j].adopt(key, buckets[i]); // std::cerr << *this << std::endl; // std::cerr << "-------" << std::endl; } hash_modulus = new_modulus; //std::cerr << *this << std::endl; //std::cerr << "-------" << std::endl; } public: ~HashTable() { if (default_init) delete default_init; } HashTable() { init(); } HashTable(VALUE const & def) { init(new VALUE(def)); } HashTable(HashTable const & a) : n_keys(a.n_keys), hash_modulus(a.hash_modulus), buckets(a.buckets), default_init(NULL) { if (a.default_init) default_init = new VALUE(*a.default_init); } void clear() { n_keys = 0; buckets.clear(); } // operator= by deep copy HashTable& operator=(HashTable const & a) { n_keys = a.n_keys; hash_modulus = a.hash_modulus; buckets = a.buckets; if (a.default_init) default_init = new VALUE(*a.default_init); else default_init = NULL; } // exists bool exists(KEY const & k) const { int i = hash(k); return buckets.exists(i) && buckets[i].exists(k); } void remove(KEY const & k) { if (exists(k)) { --n_keys; buckets[hash(k)].remove(k); } } VALUE & operator[](KEY const & k) { if (!exists(k)) { ++n_keys; if (default_init) buckets[hash(k)][k] = *default_init; if (n_keys > hash_modulus) grow(); } return buckets[hash(k)][k]; } VALUE operator[](KEY const & k) const { if (!exists(k)) die("HashTable::operator[] called with non-existent key"); return buckets[hash(k)][k]; } Array keys() const { Array keys; for (int i = 0; i < buckets.size(); ++i) { if (buckets.exists(i)) { Array bucket_keys = buckets[i].keys(); for (int j = 0; j < bucket_keys.size(); ++j) keys[keys.size()] = bucket_keys[j]; } } return keys; } Array values() const { Array values; for (int i = 0; i < buckets.size(); ++i) { if (buckets.exists(i)) { Array bucket_values = buckets[i].values(); for (int j = 0; j < bucket_values.size(); ++j) values[values.size()] = bucket_values[j]; } } return values; } void stats() const { int n = buckets.size(); int nn = 0; int s = 0; int ss = 0; if (n == 0) return; for (int i = 0; i < buckets.size(); ++i) { if (buckets.exists(i)) { int j = buckets[i].size(); ++nn; s += j; ss += j*j; } } std::cerr << n << " buckets, " << nn << " non-empty, " << s/(1.0*n) << " ave size, " << ss/(1.0*n) << " ave size^2 " << std::endl; } // output friend std::ostream & operator<< <>(std::ostream &, const HashTable &); }; // default hash function template int Hash(T const &s) { return (int) s; } // specialization for char *'s template <> int Hash(char * const & a) { char *s = a; int hash = 0; do hash += *s; while (*s++); return hash; } // operator<< template std::ostream & operator<<(std::ostream & out, const HashTable & a) { out << "n_keys = " << a.n_keys << ", hash_modulus = " << a.hash_modulus << std::endl; for (int i = 0; i < a.buckets.size(); ++i) { if (a.buckets.exists(i)) out << i << ": " << a.buckets[i]; } out << std::endl; return out; } #endif