-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathhash_table.cpp
More file actions
340 lines (285 loc) · 11 KB
/
hash_table.cpp
File metadata and controls
340 lines (285 loc) · 11 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
/*
Student Name: Benjamin Ventimiglia
Date: 3/26/19
=======================
ECE 2035 Project 2-1:
=======================
This file provides definition for the structs and functions declared in the
header file. It also contains helper functions that are not accessible from
outside of the file.
FOR FULL CREDIT, BE SURE TO TRY MULTIPLE TEST CASES and DOCUMENT YOUR CODE.
===================================
Naming conventions in this file:
===================================
1. All struct names use camel case where the first letter is capitalized.
e.g. "HashTable", or "HashTableEntry"
2. Variable names with a preceding underscore "_" will not be called directly.
e.g. "_HashTable", "_HashTableEntry"
Recall that in C, we have to type "struct" together with the name of the struct
in order to initialize a new variable. To avoid this, in hash_table.h
we use typedef to provide new "nicknames" for "struct _HashTable" and
"struct _HashTableEntry". As a result, we can create new struct variables
by just using:
- "HashTable myNewTable;"
or
- "HashTableEntry myNewHashTableEntry;"
The preceding underscore "_" simply provides a distinction between the names
of the actual struct defition and the "nicknames" that we use to initialize
new structs.
[See Hidden Definitions section for more information.]
3. Functions, their local variables and arguments are named with camel case, where
the first letter is lower-case.
e.g. "createHashTable" is a function. One of its arguments is "numBuckets".
It also has a local variable called "newTable".
4. The name of a struct member is divided by using underscores "_". This serves
as a distinction between function local variables and struct members.
e.g. "num_buckets" is a member of "HashTable".
*/
/****************************************************************************
* Include the Public Interface
*
* By including the public interface at the top of the file, the compiler can
* enforce that the function declarations in the the header are not in
* conflict with the definitions in the file. This is not a guarantee of
* correctness, but it is better than nothing!
***************************************************************************/
#include "hash_table.h"
/****************************************************************************
* Include other private dependencies
*
* These other modules are used in the implementation of the hash table module,
* but are not required by users of the hash table.
***************************************************************************/
#include <stdlib.h> // For malloc and free
#include <stdio.h> // For printf
/****************************************************************************
* Hidden Definitions
*
* These definitions are not available outside of this file. However, because
* they are forward declared in hash_table.h, the type names are
* available everywhere and user code can hold pointers to these structs.
***************************************************************************/
/**
* This structure represents an a hash table.
* Use "HashTable" instead when you are creating a new variable. [See top comments]
*/
struct _HashTable {
/** The array of pointers to the head of a singly linked list, whose nodes
are HashTableEntry objects */
HashTableEntry** buckets;
/** The hash function pointer */
HashFunction hash;
/** The number of buckets in the hash table */
unsigned int num_buckets;
};
/**
* This structure represents a hash table entry.
* Use "HashTableEntry" instead when you are creating a new variable. [See top comments]
*/
struct _HashTableEntry {
/** The key for the hash table entry */
unsigned int key;
/** The value associated with this hash table entry */
void* value;
/**
* A pointer pointing to the next hash table entry
* NULL means there is no next entry (i.e. this is the tail)
*/
HashTableEntry* next;
};
/****************************************************************************
* Private Functions
*
* These functions are not available outside of this file, since they are not
* declared in hash_table.h.
***************************************************************************/
/**
* createHashTableEntry
*
* Helper function that creates a hash table entry by allocating memory for it on
* the heap. It initializes the entry with key and value, initialize pointer to
* the next entry as NULL, and return the pointer to this hash table entry.
*
* @param key The key corresponds to the hash table entry
* @param value The value stored in the hash table entry
* @return The pointer to the hash table entry
*/
static HashTableEntry* createHashTableEntry(unsigned int key, void* value) {
// Allocate space for the new entry
HashTableEntry* newEntry = (HashTableEntry*)malloc(sizeof(HashTableEntry));
// Check if malloc failed
if (newEntry == NULL) {
printf("\tCreateHashtableEntry: Malloc has failed. Exiting.");
exit(1);
}
// Initialize new entry's values
newEntry->key = key;
newEntry->value = value;
newEntry->next = NULL;
// Return new entry
return newEntry;
}
/**
* findItem
*
* Helper function that checks whether there exists the hash table entry that
* contains a specific key.
*
* @param hashTable The pointer to the hash table.
* @param key The key corresponds to the hash table entry
* @return The pointer to the hash table entry, or NULL if key does not exist
*/
static HashTableEntry* findItem(HashTable* hashTable, unsigned int key) {
unsigned int index = hashTable->hash(key);
// Get the head entry in the correct bucket
HashTableEntry* entry = hashTable->buckets[index];
// If no entry exists, there is no list to look through
if(entry == NULL) {
return NULL;
}
// Iterate through the bucket to find the entry with the given key
while(entry != NULL) {
if(entry->key == key) {
return entry;
}
entry = entry->next;
}
// If no entry is found, the key does not exist and the function returns NULL
return NULL;
}
/****************************************************************************
* Public Interface Functions
*
* These functions implement the public interface as specified in the header
* file, and make use of the private functions and hidden definitions in the
* above sections.
****************************************************************************/
// The createHashTable is provided for you as a starting point.
HashTable* createHashTable(HashFunction hashFunction, unsigned int numBuckets) {
// The hash table has to contain at least one bucket. Exit gracefully if
// this condition is not met.
if (numBuckets==0) {
printf("Hash table has to contain at least 1 bucket...\n");
exit(1);
}
// Allocate memory for the new HashTable struct on heap.
HashTable* newTable = (HashTable*)malloc(sizeof(HashTable));
// Initialize the components of the new HashTable struct.
newTable->hash = hashFunction;
newTable->num_buckets = numBuckets;
newTable->buckets = (HashTableEntry**)malloc(numBuckets*sizeof(HashTableEntry*));
// As the new buckets contain indeterminant values, init each bucket as NULL.
unsigned int i;
for (i=0; i<numBuckets; ++i) {
newTable->buckets[i] = NULL;
}
// Return the new HashTable struct.
return newTable;
}
void destroyHashTable(HashTable* hashTable) {
// Iterate through each bucket to free up its entries
unsigned int i;
for(i = 0; i < hashTable->num_buckets; i++) {
// Now iterate through the linkedlist bucket to free each HashTableEntry
HashTableEntry* currentEntry = hashTable->buckets[i];
HashTableEntry* nextEntry;
while(currentEntry) {
nextEntry = currentEntry->next;
free(currentEntry->value);
free(currentEntry);
currentEntry = nextEntry;
}
// Destroy the bucket
hashTable->buckets[i] = NULL;
}
// Finally, free the buckets array and the table itself
free(hashTable->buckets);
free(hashTable);
}
void* insertItem(HashTable* hashTable, unsigned int key, void* value) {
// First check if the item already exists, then if its value is the same.
HashTableEntry* item = findItem(hashTable, key);
if(item != NULL) {
void* oldValue = item->value;
item->value = value;
return oldValue;
}
// Get the bucket to insert the item into
unsigned int index = hashTable->hash(key);
// Create the item
HashTableEntry* newEntry = createHashTableEntry(key, value);
// Put it at the start of the linkedlist bucket
newEntry->next = hashTable->buckets[index];
hashTable->buckets[index] = newEntry;
// Return NULL as it is a new entry
return NULL;
}
void* getItem(HashTable* hashTable, unsigned int key) {
// Find the entry
HashTableEntry* item = findItem(hashTable, key);
// If the entry exists, return its value, otherwise, return NULL
return (item) ? item->value : NULL;
}
void* removeItem(HashTable* hashTable, unsigned int key) {
// Get the head entry in the bucket
unsigned int index = hashTable->hash(key);
HashTableEntry* currentEntry = hashTable->buckets[index];
// If the head doesn't exist, return NULL
if(!currentEntry)
return NULL;
// If the head is the item we're looking for,
// set the head to the next item in the linkedlist, free the old head, and return the value
if(currentEntry->key == key) {
void* temp = currentEntry->value;
hashTable->buckets[index] = currentEntry->next;
free(currentEntry);
return temp;
}
// Otherwise, iterate through the list until we find the item
// before the one we want to delete, then
// set its next pointer to skip the deleted entry, free the entry, and finally return the value
while(currentEntry->next) {
if(currentEntry->next->key == key) {
void* tempValue = currentEntry->next->value;
HashTableEntry* temp = currentEntry->next;
currentEntry->next = currentEntry->next->next;
free(temp);
return tempValue;
}
currentEntry = currentEntry->next;
}
//If we don't find the entry, return NULL
return NULL;
}
void deleteItem(HashTable* hashTable, unsigned int key) {
// Get the head entry in the bucket
unsigned int index = hashTable->hash(key);
HashTableEntry* currentEntry = hashTable->buckets[index];
// If the head doesn't exist, return
if(!currentEntry)
return;
// If the head is the item we're looking for, free its value,
// set the head to the next item in the linkedlist, free the old head, and return
if(currentEntry->key == key) {
free(currentEntry->value);
currentEntry->value = NULL;
hashTable->buckets[index] = currentEntry->next;
free(currentEntry);
return;
}
// Otherwise, iterate through the list until we find the item
// before the one we want to delete, then free its value,
// set its next pointer to skip the deleted entry, free the entry, and finally return
while(currentEntry->next) {
if(currentEntry->next->key == key) {
free(currentEntry->next->value);
HashTableEntry* temp = currentEntry->next;
currentEntry->next = currentEntry->next->next;
free(temp);
return;
}
currentEntry = currentEntry->next;
}
//If we don't find the entry, return
return;
}