1. 程式人生 > >C++模板類實現hashMap

C++模板類實現hashMap

非常常見的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的改變