C++模板類實現hashMap
阿新 • • 發佈:2018-11-20
非常常見的hashMap,具體介紹可以自己百度,這裡就不詳細介紹了
主要採用的是拉鍊法,然後hash函式採用的是C++自帶的hash函式庫
標頭檔案
1 #ifndef MAP_H 2 #define MAP_H 3 4 // Hash table implementation of UnorderedMap ADT. The container is generic 5 // Use external chaining with linked lists for collision resolution 6 7 #include <cstdlib> 8#include <functional> // for std::hash<T> 9 #include <iostream> 10 11 using namespace std; 12 13 template<typename KEY, typename T, typename H = std::hash<KEY> > 14 class UnorderedMap { 15 private: 16 struct Entry { 17 18 Entry() : isBucket(true) { 19 20 } 21 22 Entry(KEY newKey, T newData) : key(newKey), data(newData), isBucket(true) {} 23 24 KEY key; 25 T data; 26 bool isBucket; 27 Entry *next; 28 }; 29 30 Entry **array; // pointers to the hash table, each table entry is a pointer that used to point to a linked list of Entries.31 int tablesize; // table size 32 int entrysize; // total number of entries 33 void rehash(); // double the hashtable when the load factor reaches 2. 34 public: 35 static Entry endElem; 36 37 // Constructs an empty unordered_map object, containing no elements and with a entry size of zero 38 // Notice the hash table with a default size 101 is constructed but empty. 39 UnorderedMap(); 40 41 // Copy constructor The object is initialized to have the same contents and properties as the UnorderedMap object rhs. 42 UnorderedMap(const UnorderedMap &rhs); 43 44 // Assignment operator 45 UnorderedMap &operator=(const UnorderedMap &rhs); 46 47 // Destructs the container object. This calls each of the contained element's destructors, 48 // and dealocates all the storage capacity allocated by the UnorderedMap container. 49 ~UnorderedMap(); 50 51 // A simple forward iterator, traverse the collection in one direction. The order of traversal is not specified. 52 // But, in any case, the range that goes from its begin to its end covers all the elements in the container. 53 class Iterator { 54 public: 55 Iterator() {} 56 57 explicit Iterator(Entry *cur) : _cur(cur) {} 58 59 Entry &operator*(); 60 61 Entry *operator->(); 62 63 Iterator operator++(int); 64 65 bool operator!=(Iterator); 66 67 bool operator==(Iterator); 68 69 private: 70 Entry *_cur; 71 }; 72 73 // Returns an iterator pointing to the first element in the UnorderedMap container 74 // Note that an UnorderedMap object makes no guarantees on which specific element is considered its first element. 75 // But, in any case, the range that goes from its begin to its end covers all the elements in the container. 76 Iterator begin() const; 77 78 // Returns an iterator pointing to the past-the-end element in the unordered_map container 79 // Note that an UnorderedMap object makes no guarantees on which order its elements follow. 80 // But, in any case, the range that goes from its begin to its end covers all the elements in the container. 81 Iterator end() const; 82 83 // Inserts new elements in the unordered_map. 84 // Each element is inserted only if its key is not equivalent to the key of any other element already in the container 85 // (keys in an UnorderedMap are unique). function returns true if new element is inserted, false otherwise. 86 bool insert(const KEY &, const T &); 87 88 // Removes from the UnorderedMap container a single element 89 // retur true if the element is erased, false otherwise 90 bool erase(const KEY &); 91 92 // Searches the container for an element with the given key and returns an iterator to it if found, 93 // otherwise it returns an iterator to UnorderedMap::end() (the element past the end of the container). 94 Iterator find(const KEY &) const; 95 96 // If k matches the key of an element in the container, the function returns a reference to its mapped value. 97 // If k does not match the key of any element in the container, the function inserts a new element with that key 98 // and returns a reference to its mapped value. Notice that this always increases the container size by one, 99 // even if no mapped value is assigned to the element (the element is constructed using its default constructor). 100 T &operator[](const KEY &); 101 102 // Returns the number of elements in the UnorderedMap container. 103 int size() { return entrysize; } 104 105 // Returns the table size; included for verifying rehash operation. 106 int tsize() { return tablesize; } 107 }; 108 109 // check if the number n is prime 110 bool isPrime(int n); 111 112 // find the next prime number following given n 113 int nextPrime(int n); 114 115 #include "unorderedmap.cpp" // must include source file for template compilation 116 117 #endif
具體實現
1 // Below are the stubs for SOME methods 2 // Make sure all methods in unorderedmap.h are implemented 3 #include "unorderedmap.h" 4 5 template<typename KEY, typename T, typename H> 6 UnorderedMap<KEY, T, H>::UnorderedMap() { 7 array = new Entry *[101]; 8 tablesize = 101; 9 entrysize = 0; 10 for (int i = 0; i < tablesize; ++i) { 11 array[i] = new Entry; 12 array[i]->isBucket = true; 13 } 14 15 for (int i = 0; i < tablesize - 1; ++i) { 16 array[i]->next = array[i + 1]; 17 } 18 array[tablesize - 1]->next = NULL; 19 } 20 21 template<typename KEY, typename T, typename H> 22 UnorderedMap<KEY, T, H>::~UnorderedMap() { 23 for (int i = 0; i < tablesize; ++i) { 24 Entry *p = array[i]->next; 25 while (p != NULL && !p->isBucket) { 26 Entry *temp = p; 27 p = p->next; 28 delete (temp); 29 } 30 } 31 32 for (int i = 0; i < tablesize; ++i) { 33 delete (array[i]); 34 } 35 delete[]array; 36 } 37 38 template<typename KEY, typename T, typename H> 39 bool UnorderedMap<KEY, T, H>::insert(const KEY &newkey, const T &data) { 40 41 if (find(newkey) != end()) { 42 return false; 43 } 44 45 if (tablesize * 2 < entrysize) { 46 rehash(); 47 } 48 std::size_t key = H()(newkey); 49 int h = key % tablesize; 50 Entry *e = new Entry(newkey, data); 51 e->isBucket = false; 52 e->next = array[h]->next; 53 array[h]->next = e; 54 ++entrysize; 55 return true; 56 } 57 58 template<typename KEY, typename T, typename H> 59 void UnorderedMap<KEY, T, H>::rehash() { 60 int oldTableSize = tablesize; 61 Entry **oldArray = array; 62 63 tablesize = nextPrime(tablesize * 2); 64 array = new Entry *[tablesize]; 65 66 for (int i = 0; i < tablesize; ++i) { 67 array[i] = new Entry; 68 } 69 70 for (int i = 0; i < tablesize - 1; ++i) { 71 array[i]->next = array[i + 1]; 72 } 73 array[tablesize - 1]->next = NULL; 74 75 entrysize = 0; 76 for (int i = 0; i < oldTableSize; ++i) { 77 Entry *p = oldArray[i]->next; 78 while (p != NULL && !p->isBucket) { 79 insert(p->key, p->data); 80 p = p->next; 81 } 82 } 83 84 for (int i = 0; i < oldTableSize; ++i) { 85 Entry *p = oldArray[i]->next; 86 while (p != NULL && !p->isBucket) { 87 Entry *temp = p; 88 p = p->next; 89 delete (temp); 90 } 91 } 92 93 for (int i = 0; i < oldTableSize; ++i) { 94 delete (oldArray[i]); 95 } 96 delete[]oldArray; 97 } 98 99 template<typename KEY, typename T, typename H> 100 typename UnorderedMap<KEY, T, H>::Iterator UnorderedMap<KEY, T, H>::find(const KEY &x) const { 101 // stub code; need to implement !!! 102 std::size_t key = H()(x); 103 int h = key % tablesize; 104 Entry *p = array[h]->next; 105 while (p != NULL && !p->isBucket) { 106 if (p->key == x) { 107 return Iterator(p); 108 } 109 p = p->next; 110 } 111 return end(); 112 } 113 114 115 template<typename KEY, typename T, typename H> 116 T &UnorderedMap<KEY, T, H>::operator[](const KEY &k) { 117 // stub code; need to implement !!! 118 std::size_t key = H()(k); 119 int h = key % tablesize; 120 Entry *p = array[h]->next; 121 while (p != NULL && !p->isBucket) { 122 if (p->key == k) { 123 return p->data; 124 } 125 p = p->next; 126 } 127 if (tablesize < entrysize * 2) { 128 rehash(); 129 } 130 ++entrysize; 131 h = key % tablesize; 132 p = array[h]; 133 Entry *e = new Entry(); 134 e->key = k; 135 e->isBucket = false; 136 e->next = p->next; 137 p->next = e; 138 return e->data; 139 } 140 141 template<typename KEY, typename T, typename H> 142 typename UnorderedMap<KEY, T, H>::Iterator UnorderedMap<KEY, T, H>::Iterator::operator++(int) { 143 while (_cur != NULL) { 144 _cur = _cur->next; 145 if (_cur == NULL) { 146 return Iterator(NULL); 147 } 148 149 if (!_cur->isBucket) { 150 return Iterator(_cur); 151 } 152 } 153 return Iterator(NULL); 154 } 155 156 template<typename KEY, typename T, typename H> 157 typename UnorderedMap<KEY, T, H>::Iterator UnorderedMap<KEY, T, H>::begin() const { 158 // stub code; need to implement !!! 159 Entry *p = array[0]->next; 160 while (p != NULL) { 161 if (!p->isBucket) { 162 return Iterator(p); 163 } 164 p = p->next; 165 } 166 return end(); 167 } 168 169 template<typename KEY, typename T, typename H> 170 bool UnorderedMap<KEY, T, H>::erase(const KEY &k) { 171 if (find(k) == end()) {//not found 172 return false; 173 } 174 std::size_t key = H()(k); 175 int h = key % tablesize; 176 Entry *p1 = array[h]; 177 Entry *p2 = p1->next; 178 --entrysize; 179 180 while (p2 != NULL && !p2->isBucket) { 181 if (p2->key == k) { 182 p1->next = p2->next; 183 delete (p2); 184 return true; 185 } 186 p1 = p2; 187 p2 = p2->next; 188 } 189 return true; 190 } 191 192 template<typename KEY, typename T, typename H> 193 typename UnorderedMap<KEY, T, H>::Iterator UnorderedMap<KEY, T, H>::end() const { 194 return Iterator(NULL); 195 } 196 197 template<typename KEY, typename T, typename H> 198 typename UnorderedMap<KEY, T, H>::Entry *UnorderedMap<KEY, T, H>::Iterator::operator->() { 199 return _cur; 200 } 201 202 template<typename KEY, typename T, typename H> 203 typename UnorderedMap<KEY, T, H>::Entry &UnorderedMap<KEY, T, H>::Iterator::operator*() { 204 return *_cur; 205 } 206 207 208 template<typename KEY, typename T, typename H> 209 bool UnorderedMap<KEY, T, H>::Iterator::operator!=(Iterator it) { 210 return _cur != it._cur; 211 } 212 213 template<typename KEY, typename T, typename H> 214 bool UnorderedMap<KEY, T, H>::Iterator::operator==(Iterator it) { 215 return _cur == it._cur; 216 } 217 218 // Internal method to test if a positive number is prime (not efficient) 219 bool isPrime(int n) { 220 if (n == 2 || n == 3) 221 return true; 222 if (n == 1 || n % 2 == 0) 223 return false; 224 for (int i = 3; i * i <= n; i += 2) 225 if (n % i == 0) 226 return false; 227 return true; 228 } 229 230 // Internal method to return a prime number at least as large as n. 231 // Assumes n > 0. 232 int nextPrime(int n) { 233 if (n % 2 == 0) 234 n++; 235 for (; !isPrime(n); n += 2); 236 return n; 237 }
總結
我在寫的時候有這樣幾個問題
- 記憶體釋放,這份地方一定要仔細,可以使用valgrind進行檢查。
- rehash的時候,我才用的是通過重用insert的方式,這裡一定要注意,tableSize,entrySize的改變