Data Structures 18 - Hash Table
A hash table is a highly efficient data structure primarily used for fast lookup, insertion, and deletion operations. It maps keys to specific positions in the table via a hash function, thereby accelerating data access. Below is a detailed and systematic guide to hash table implementation, covering basic concepts, hash functions, collision resolution methods, and concrete implementation in C.
1. Basic Concepts
- Hash Function: A function that maps an input (key) to an index position in the hash table.
- Hash Table: An array used to store key-value pairs.
- Collision: When different keys are mapped to the same index position by the hash function.
- Load Factor: The ratio of the number of elements in the hash table to the size of the hash table, used to measure how full the hash table is.
2. Hash Functions
The choice of hash function has a significant impact on the performance of a hash table. A good hash function should distribute keys uniformly and minimize collisions. Common hash functions include:
unsigned int hash_function(const char *key) {
unsigned int hash = 0;
while (*key) {
hash = (hash << 5) + *key++;
}
return hash;
}
3. Collision Resolution Methods
There are two common collision resolution methods:
- Chaining: Each index position in the hash table stores a linked list. When a collision occurs, the new element is added to the linked list.
- Open Addressing: When a collision occurs, the next available position is sought to store the element.
4. Concrete Implementation in C
4.1 Defining the Structure
First, define the hash table structure, including the linked list node and the hash table itself.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define TABLE_SIZE 100
typedef struct HashNode {
char *key;
int value;
struct HashNode *next;
} HashNode;
typedef struct HashTable {
HashNode **table;
} HashTable;
4.2 Initializing the Hash Table
HashTable* create_table() {
HashTable *hashTable = (HashTable*) malloc(sizeof(HashTable));
hashTable->table = (HashNode**) malloc(TABLE_SIZE * sizeof(HashNode*));
for (int i = 0; i < TABLE_SIZE; i++) {
hashTable->table[i] = NULL;
}
return hashTable;
}
4.3 Inserting Elements
void insert(HashTable *hashTable, const char *key, int value) {
unsigned int index = hash_function(key) % TABLE_SIZE;
HashNode *newNode = (HashNode*) malloc(sizeof(HashNode));
newNode->key = strdup(key);
newNode->value = value;
newNode->next = hashTable->table[index];
hashTable->table[index] = newNode;
}
4.4 Searching for Elements
int search(HashTable *hashTable, const char *key) {
unsigned int index = hash_function(key) % TABLE_SIZE;
HashNode *node = hashTable->table[index];
while (node != NULL) {
if (strcmp(node->key, key) == 0) {
return node->value;
}
node = node->next;
}
return -1; // Not found
}
4.5 Deleting Elements
void delete(HashTable *hashTable, const char *key) {
unsigned int index = hash_function(key) % TABLE_SIZE;
HashNode *node = hashTable->table[index];
HashNode *prev = NULL;
while (node != NULL && strcmp(node->key, key) != 0) {
prev = node;
node = node->next;
}
if (node == NULL) {
return; // Key not found
}
if (prev == NULL) {
hashTable->table[index] = node->next;
} else {
prev->next = node->next;
}
free(node->key);
free(node);
}
4.6 Freeing the Hash Table
void free_table(HashTable *hashTable) {
for (int i = 0; i < TABLE_SIZE; i++) {
HashNode *node = hashTable->table[i];
while (node != NULL) {
HashNode *temp = node;
node = node->next;
free(temp->key);
free(temp);
}
}
free(hashTable->table);
free(hashTable);
}
5. Example Code
int main() {
HashTable *hashTable = create_table();
insert(hashTable, "key1", 1);
insert(hashTable, "key2", 2);
insert(hashTable, "key3", 3);
printf("Value for 'key1': %d\n", search(hashTable, "key1"));
printf("Value for 'key2': %d\n", search(hashTable, "key2"));
printf("Value for 'key3': %d\n", search(hashTable, "key3"));
delete(hashTable, "key2");
printf("Value for 'key2' after deletion: %d\n", search(hashTable, "key2"));
free_table(hashTable);
return 0;
}
Written by Professor Min Fan
Code
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define TABLE_SIZE 100 // Size of the hash table
#define KEY_SIZE 50 // Maximum length of a key
#define VALUE_SIZE 100 // Maximum length of a value
typedef struct KeyValuePair {
char key[KEY_SIZE];
char value[VALUE_SIZE];
struct KeyValuePair *next; // Pointer to the next node, handles collisions
} KeyValuePair;
typedef struct {
KeyValuePair *table[TABLE_SIZE]; // Hash table array
} HashTable;
// Initialize the hash table
void initHashTable(HashTable *hashTable) {
for (int i = 0; i < TABLE_SIZE; i++) {
hashTable->table[i] = NULL;
}
}
Output
Collision, move forward for key 57.
Collision, move forward for key 95.
Collision, move forward for key 95.
Search result of 95 is: r
Search result of 38 is: l
Search result of 57 is: w
Search result of 4 is: x
Press any key to continue