I've been writing from scratch a C implementation for a dynamically sized hash table. I made a crucial error in that my hashing function was based on the capacity of the hash table. Since the capacity changes over times this doesn't work. What are the recommendations for developing a hashing function for a dynamically allocated hash table?
Additionally, I am using quadratic probing so my resizing is based on that. For example, if my hash table capacity was 8, and a new key originally hashed to index 0, my new indexes calculated would be 1 (0 + 1^2), 5 (1 + 2^2), 14 (5 + 3^2) etc using quadratic probingand I would stop at 14 since that's larger than 8. So, I'd create a new hash table of capacity 15. I would like to keep this implementation, but if there's a better way I'm open to changing it. Regardless, I'm still looking for how to develop a hash function for a dynamic array, not static.
EDITS: What I mean is because my hashing function is based on the capacity of the hash table, when I go to retrieve an element AFTER the table size has changed, it doesn't work. For example, in my main program I go to remove the element with a key of "A", and print out the table again but A is still there. This is because I used my hashing function to find where "A" existed in order to remove it, but the hashing function is different when I go to remove "A" because when I inserted "A", the capacity was different than when I tried to remove it. So, the hashing function didn't lead me to the right place.
I've read something about when I resize the hash table, I just have to rehash all of the elements currently in the hash table with the size of the new hash table. I was just wondering if there's another way to do it other than that.
status.h
#ifndef STATUS_H
#define STATUS_H
typedef enum status { FAILURE, SUCCESS } Status;
typedef enum boolean { FALSE, TRUE } Boolean;
#endif
HashTableElement.h
#ifndef KEY_AND_DATA_H
#define KEY_AND_DATA_H
#include "status.h"
typedef void* HASH_TABLE_ELEMENT;
/*Precondition: none
Postcondition: returns a handle to a new hash table element. Else returns NULL */
HASH_TABLE_ELEMENT hash_table_element_create(char* key, int data);
/*Precondition: hHash_table_element is a handle to a valid hash table element, data is the
new data value.
Postcondition: the data inside the hash table has been updated. */
void hash_table_element_update(HASH_TABLE_ELEMENT hHash_table_element, int data);
/*Precondition: hHash_table_element is a handle to a valid hash table element.
Postcondition: returns the data value. */
int hash_table_element_get_data(HASH_TABLE_ELEMENT hHash_table_element);
/*Precondition: hHash_table_element is a handle to a valid hash table element.
Postcondition: returns the key */
const char* hash_table_element_get_key(HASH_TABLE_ELEMENT hHash_table_element);
/*Precondition: hHash_table_element1 and 2 are handles to valid hash table elements.
Postcondition: returns true or false if the keys match or not*/
Boolean hash_table_element_keys_match(HASH_TABLE_ELEMENT hHash_table_element1,
HASH_TABLE_ELEMENT hHash_table_element2);
Status hash_table_element_get_character_by_index(HASH_TABLE_ELEMENT hHash_table_element, int index, char* ch);
void hash_table_element_destroy(HASH_TABLE_ELEMENT* phHash_table_element);
#endif
HashTable.h
#ifndef HASH_TABLE_H
#define HASH_TABLE_H
#include "status.h"
typedef void* HASH_TABLE;
/* Precondition: none
Postcondition: returns a handle to an empty hash table or NULL on Failure */
HASH_TABLE hash_table_init_default(unsigned initial_capacity);
/* Precondition: capacity is the capacity of the hash table.
key is the key to be hased.
Postcondition: returns an index in the hash table that comes from
hasing the key with the hash table capacity */
unsigned hash_table_hash(unsigned capacity, char* key);
/* Precondition: hHash_table is a handle to a valid hash_table
Postcondition: returns the capacity */
unsigned hash_table_get_capacity(HASH_TABLE hHash_table);
/* Precondition: hHash_table is a handle to a valid hash table. Key and data
are the info to be put into the hash_table
Postcondition: a new element has been created and inserted in the hash table
Returns FAILURE for any memory allocation failure */
Status hash_table_insert(HASH_TABLE hHash_table, char* key, int data);
/* Precondition: hHash_table is a handle to a valid hash table object. Key is the
key to search for.
Postcondition: if the key exists, stores it in data and returns SUCCESS. Else,
returns FAILURE and stores a 0 in data */
Status hash_table_get_data_by_key(HASH_TABLE hHash_table, char* key, int* data);
/* Precondition: hHash_table is a handle to a hash table. key is the key to be looked for.
Postcondition: if the key exists, stores the index in indexOfKey and returns true. If it
doesn't, returns false and stors a 0 in indexOfKey */
Boolean hash_table_get_key_index(HASH_TABLE hHash_table, char* key, unsigned* indexOfKey);
/* Precondition: hHash_table is a handle to a hash table. Index is the index to search.
Data stores the data at the index.
Postcondition: returns SUCCESS and stores the data value at that index in data. If the index
caused overflow, or the index was NULL, returns FAILIURE and data is set to 0 */
Status hash_table_get_data_by_index(HASH_TABLE hHash_table, int index, int* data);
/* Precondition: hHash_table is a handle to a hash table. Index is the index to search.
Data stores the data at the index.
Postcondition: returns SUCCESS and stores the key at that index in key. If the index
caused overflow, or the index was NULL, returns FAILIURE and key is set as the empty string */
Status hash_table_get_key_by_index(HASH_TABLE hHash_table, int index, char* key);
/* Precondition: hHash_table is a handle to a valid hash table object. Key is the
key to be searched for
Postcondition: if the element corresponding to the key exists, it is removed and
SUCCESS is returned. Else, it FAILURE is returned */
Status hash_table_remove_element(HASH_TABLE hHash_table, char* key);
/* Precondition: phHash_table is a pointer to a handle to a hash table
Postcondion: all memory associated with the hash table has been freed.
and the hash table handle is set to NULL */
void hash_table_destroy(HASH_TABLE* phHash_table);
void debug(HASH_TABLE hHash_table);
#endif
HashTableElement.c
#include "HashTableElement.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct hash_table_element {
char* key;
int data;
unsigned capacity; // capacity of hash table during creation
} Hash_table_element;
HASH_TABLE_ELEMENT hash_table_element_create(char* key, int data) {
Hash_table_element* pHash_table_element = (Hash_table_element*)malloc(sizeof(Hash_table_element));
if (pHash_table_element != NULL) {
pHash_table_element->key = (char*)malloc(sizeof(char) * (strlen(key) + 1));
if (pHash_table_element->key == NULL) {
free(pHash_table_element);
return NULL;
}
for (unsigned i = 0; i < strlen(key); i++)
pHash_table_element->key[i] = key[i];
pHash_table_element->key[strlen(key)] = '\0';
pHash_table_element->data = data;
}
return (HASH_TABLE_ELEMENT)pHash_table_element;
}
void hash_table_element_update(HASH_TABLE_ELEMENT hHash_table_element, int data) {
Hash_table_element* pHash_table_element = (Hash_table_element*)hHash_table_element;
pHash_table_element->data = data;
}
int hash_table_element_get_data(HASH_TABLE_ELEMENT hHash_table_element) {
Hash_table_element* pHash_table_element = (Hash_table_element*)hHash_table_element;
return pHash_table_element->data;
}
const char* hash_table_element_get_key(HASH_TABLE_ELEMENT hHash_table_element) {
Hash_table_element* pHash_table_element = (Hash_table_element*)hHash_table_element;
return (const char*)pHash_table_element->key;
}
Boolean hash_table_element_keys_match(HASH_TABLE_ELEMENT hHash_table_element1,
HASH_TABLE_ELEMENT hHash_table_element2) {
Hash_table_element* pHash_table_element1 = (Hash_table_element*)hHash_table_element1;
Hash_table_element* pHash_table_element2 = (Hash_table_element*)hHash_table_element2;
if (!strcmp(pHash_table_element1->key, pHash_table_element2->key))
return TRUE;
return FALSE;
}
Status hash_table_element_get_character_by_index(HASH_TABLE_ELEMENT hHash_table_element, int index, char* ch) {
Hash_table_element* pHash_table_element = (Hash_table_element*)hHash_table_element;
if (index > strlen(pHash_table_element->key)) {
*ch = '\0';
return FAILURE;
}
*ch = pHash_table_element->key[index];
return SUCCESS;
}
void hash_table_element_destroy(HASH_TABLE_ELEMENT* phHash_table_element) {
if (*phHash_table_element != NULL) {
Hash_table_element* pHash_table_element = (Hash_table_element*)*phHash_table_element;
free(pHash_table_element->key);
free(pHash_table_element);
*phHash_table_element = NULL;
}
}
HashTable.c
#include "HashTable.h"
#include "HashTableElement.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct hash_table {
HASH_TABLE_ELEMENT* table;
unsigned capacity;
} Hash_table;
HASH_TABLE hash_table_init_default(unsigned initial_capacity) {
Hash_table* pHash_table = (Hash_table*)malloc(sizeof(Hash_table));
if (pHash_table != NULL) {
pHash_table->table = (HASH_TABLE_ELEMENT*)malloc(sizeof(HASH_TABLE_ELEMENT) * initial_capacity);
if (pHash_table->table == NULL) {
free(pHash_table);
return NULL;
}
for (unsigned i = 0; i < initial_capacity; i++) {
pHash_table->table[i] = NULL;
}
pHash_table->capacity = initial_capacity;
}
return (HASH_TABLE)pHash_table;
}
unsigned hash_table_hash(unsigned capacity, char* key) {
unsigned sum = 0;
for (unsigned i = 0; i < strlen(key); i++)
sum += key[i];
return sum % capacity;
}
unsigned hash_table_get_capacity(HASH_TABLE hHash_table) {
Hash_table* pHash_table = (Hash_table*)hHash_table;
return pHash_table->capacity;
}
Status hash_table_insert(HASH_TABLE hHash_table, char* key, int data) {
Hash_table* pHash_table = (Hash_table*)hHash_table;
unsigned index = hash_table_hash(pHash_table->capacity, key);
unsigned quadraticNum = 1;
Boolean overflow = (Boolean)(index >= pHash_table->capacity);
while (!overflow && pHash_table->table[index] != NULL) {
if (!strcmp(hash_table_element_get_key(pHash_table->table[index]), key)) {
hash_table_element_update(pHash_table->table[index], data);
return SUCCESS;
}
else {
index += quadraticNum * quadraticNum;
quadraticNum++;
if (index >= pHash_table->capacity) {
overflow = TRUE;
}
}
}
if (overflow) {
unsigned newCapacity = index + 1;
HASH_TABLE_ELEMENT* newTable = (HASH_TABLE_ELEMENT*)malloc(sizeof(HASH_TABLE_ELEMENT) * newCapacity);
if (newTable == NULL)
return FAILURE;
for (unsigned i = 0; i < pHash_table->capacity; i++) {
if (pHash_table->table[i] == NULL)
newTable[i] = NULL;
else {
newTable[i] =
hash_table_element_create(hash_table_element_get_key(pHash_table->table[i]),
hash_table_element_get_data(pHash_table->table[i]));
if (newTable[i] == NULL) {
for (int j = i - 1; j >= 0; j--)
hash_table_element_destroy(&(newTable[j]));
free(newTable);
return FAILURE;
}
}
}
for (unsigned i = pHash_table->capacity; i < newCapacity - 1; i++)
newTable[i] = NULL;
newTable[newCapacity - 1] = hash_table_element_create(key, data, pHash_table->capacity);
if (newTable[newCapacity - 1] == NULL) {
for (int i = newCapacity - 2; i >= 0; i--)
hash_table_element_destroy(&(newTable[i]));
free(newTable);
return FAILURE;
}
for (unsigned i = 0; i < pHash_table->capacity; i++)
hash_table_element_destroy(&(pHash_table->table[i]));
free(pHash_table->table);
pHash_table->table = newTable;
pHash_table->capacity = newCapacity;
return SUCCESS;
}
else {
pHash_table->table[index] = hash_table_element_create(key, data, pHash_table->capacity);
if (pHash_table->table[index] == NULL)
return FAILURE;
return SUCCESS;
}
}
Boolean hash_table_get_key_index(HASH_TABLE hHash_table, char* key, unsigned* indexOfKey) {
Hash_table* pHash_table = (Hash_table*)hHash_table;
unsigned index = hash_table_hash(pHash_table->capacity, key);
unsigned quadraticNum = 1;
while (index < pHash_table->capacity) {
if (pHash_table->table[index] != NULL) {
if (!strcmp(key, hash_table_element_get_key(pHash_table->table[index]))) {
*indexOfKey = index;
return TRUE;
}
}
index += quadraticNum * quadraticNum;
quadraticNum++;
}
*indexOfKey = 0;
return FALSE;
}
Status hash_table_get_data_by_key(HASH_TABLE hHash_table, char* key, int* data) {
unsigned indexOfKey = 0;
if (hash_table_get_key_index(hHash_table, key, &indexOfKey)) {
Hash_table* pHash_table = (Hash_table*)hHash_table;
*data = hash_table_element_get_data(pHash_table->table[indexOfKey]);
return SUCCESS;
}
*data = 0;
return FAILURE;
}
Status hash_table_get_data_by_index(HASH_TABLE hHash_table, int index, int* data) {
Hash_table* pHash_table = (Hash_table*)hHash_table;
if (index >= pHash_table->capacity || pHash_table->table[index] == NULL) {
*data = 0;
return FAILURE;
}
*data = hash_table_element_get_data(pHash_table->table[index]);
return SUCCESS;
}
Status hash_table_get_key_by_index(HASH_TABLE hHash_table, int index, char* key) {
Hash_table* pHash_table = (Hash_table*)hHash_table;
if (index >= pHash_table->capacity || pHash_table->table[index] == NULL) {
key[0] = '\0';
return FAILURE;
}
char ch;
for (unsigned i = 0; i < strlen(hash_table_element_get_key(pHash_table->table[index])); i++) {
hash_table_element_get_character_by_index(pHash_table->table[index], i, &key[i]);
}
key[strlen(hash_table_element_get_key(pHash_table->table[index]))] = '\0';
return SUCCESS;
}
Status hash_table_remove_element(HASH_TABLE hHash_table, char* key) {
unsigned indexOfKey = 0;
if (hash_table_get_key_index(hHash_table, key, &indexOfKey)) {
Hash_table* pHash_table = (Hash_table*)hHash_table;
hash_table_element_destroy(&(pHash_table->table[indexOfKey]));
return SUCCESS;
}
return FAILURE;
}
void hash_table_destroy(HASH_TABLE* phHash_table) {
Hash_table* pHash_table = (Hash_table*)*phHash_table;
for (unsigned i = 0; i < pHash_table->capacity; i++)
hash_table_element_destroy(&(pHash_table->table[i]));
free(pHash_table->table);
free(pHash_table);
*phHash_table = NULL;
}
void debug(HASH_TABLE hHash_table) {
Hash_table* pHash_table = (Hash_table*)hHash_table;
int data;
char key[100];
char DNE[4] = "DNE";
for (unsigned i = 0; i < pHash_table->capacity; i++) {
printf("Index: %-10d", i);
Status keyStatus = hash_table_get_key_by_index(hHash_table, i, key);
Status dataStatus = hash_table_get_data_by_index(hHash_table, i, &data);
if (keyStatus == FAILURE && dataStatus == FAILURE) {
printf("Key: %-10sData: %-10s\n", DNE, DNE);
}
else {
printf("Key: %-10sData: %-10d\n", key, data);
}
}
}
main.c
#include <stdio.h>
#include "HashTable.h"
#include <string.h>
#include <vld.h>
int main(int argc, char** argv) {
HASH_TABLE hHash_table = hash_table_init_default(5);
char key[3] = "A";
unsigned num = 1;
for (unsigned i = 0; i < 26; i++) {
hash_table_insert(hHash_table, key, num);
key[0] = key[0] + 1;
num++;
}
debug(hHash_table);
printf("\n\n\n");
hash_table_remove_element(hHash_table, "A");
debug(hHash_table);
hash_table_destroy(&hHash_table);
return 0;
}
Output
Visual Leak Detector read settings from: C:\Program Files (x86)\Visual Leak Detector\vld.ini
Visual Leak Detector Version 2.5.1 installed.
Index: 0 Key: A Data: 1
Index: 1 Key: B Data: 2
Index: 2 Key: C Data: 3
Index: 3 Key: D Data: 4
Index: 4 Key: E Data: 5
Index: 5 Key: F Data: 6
Index: 6 Key: G Data: 7
Index: 7 Key: H Data: 8
Index: 8 Key: S Data: 19
Index: 9 Key: Q Data: 17
Index: 10 Key: J Data: 10
Index: 11 Key: K Data: 11
Index: 12 Key: L Data: 12
Index: 13 Key: M Data: 13
Index: 14 Key: N Data: 14
Index: 15 Key: I Data: 9
Index: 16 Key: O Data: 15
Index: 17 Key: P Data: 16
Index: 18 Key: V Data: 22
Index: 19 Key: W Data: 23
Index: 20 Key: X Data: 24
Index: 21 Key: Y Data: 25
Index: 22 Key: Z Data: 26
Index: 23 Key: T Data: 20
Index: 24 Key: R Data: 18
Index: 25 Key: DNE Data: DNE
Index: 26 Key: DNE Data: DNE
Index: 27 Key: DNE Data: DNE
Index: 28 Key: DNE Data: DNE
Index: 29 Key: DNE Data: DNE
Index: 30 Key: DNE Data: DNE
Index: 31 Key: DNE Data: DNE
Index: 32 Key: DNE Data: DNE
Index: 33 Key: DNE Data: DNE
Index: 34 Key: DNE Data: DNE
Index: 35 Key: DNE Data: DNE
Index: 36 Key: DNE Data: DNE
Index: 37 Key: DNE Data: DNE
Index: 38 Key: DNE Data: DNE
Index: 39 Key: DNE Data: DNE
Index: 40 Key: U Data: 21
Index: 0 Key: A Data: 1
Index: 1 Key: B Data: 2
Index: 2 Key: C Data: 3
Index: 3 Key: D Data: 4
Index: 4 Key: E Data: 5
Index: 5 Key: F Data: 6
Index: 6 Key: G Data: 7
Index: 7 Key: H Data: 8
Index: 8 Key: S Data: 19
Index: 9 Key: Q Data: 17
Index: 10 Key: J Data: 10
Index: 11 Key: K Data: 11
Index: 12 Key: L Data: 12
Index: 13 Key: M Data: 13
Index: 14 Key: N Data: 14
Index: 15 Key: I Data: 9
Index: 16 Key: O Data: 15
Index: 17 Key: P Data: 16
Index: 18 Key: V Data: 22
Index: 19 Key: W Data: 23
Index: 20 Key: X Data: 24
Index: 21 Key: Y Data: 25
Index: 22 Key: Z Data: 26
Index: 23 Key: T Data: 20
Index: 24 Key: R Data: 18
Index: 25 Key: DNE Data: DNE
Index: 26 Key: DNE Data: DNE
Index: 27 Key: DNE Data: DNE
Index: 28 Key: DNE Data: DNE
Index: 29 Key: DNE Data: DNE
Index: 30 Key: DNE Data: DNE
Index: 31 Key: DNE Data: DNE
Index: 32 Key: DNE Data: DNE
Index: 33 Key: DNE Data: DNE
Index: 34 Key: DNE Data: DNE
Index: 35 Key: DNE Data: DNE
Index: 36 Key: DNE Data: DNE
Index: 37 Key: DNE Data: DNE
Index: 38 Key: DNE Data: DNE
Index: 39 Key: DNE Data: DNE
Index: 40 Key: U Data: 21
No memory leaks detected.
Visual Leak Detector is now exiting.
C:\UML\Computer Science\COMP.1020 Computing II\Interfaces\Hash Table ADT\No Duplicates\Hash Table ADT\Debug\Hash Table ADT.exe (process 24304) exited with code 0.
Press any key to close this window . . .