#include #include #include #define TABLE_SIZE 10 // Node structure for Separate Chaining struct Node char key[50]; char value[100]; struct Node* next; ; // Hash Table structure struct HashTable struct Node* bucket[TABLE_SIZE]; ; // A simple Hash Function (DJB2 algorithm style) unsigned int hash(char* key) unsigned int hashValue = 0; for (int i = 0; key[i] != '\0'; i++) hashValue = (hashValue << 5) + key[i]; return hashValue % TABLE_SIZE; // Create a new node struct Node* createNode(char* key, char* value) struct Node* newNode = (struct Node*)malloc(sizeof(struct Node)); strcpy(newNode->key, key); strcpy(newNode->value, value); newNode->next = NULL; return newNode; // Insert into Dictionary void insert(struct HashTable* ht, char* key, char* value) unsigned int index = hash(key); struct Node* newNode = createNode(key, value); if (ht->bucket[index] == NULL) ht->bucket[index] = newNode; else // Handle collision via Separate Chaining (insert at head) newNode->next = ht->bucket[index]; ht->bucket[index] = newNode; printf("Inserted: [%s : %s]\n", key, value); // Search for a key void search(struct HashTable* ht, char* key) unsigned int index = hash(key); struct Node* temp = ht->bucket[index]; while (temp != NULL) if (strcmp(temp->key, key) == 0) printf("Found: %s -> %s\n", key, temp->value); return; temp = temp->next; printf("Error: Key '%s' not found.\n", key); // Main Driver Code int main() struct HashTable ht; // Initialize buckets to NULL for (int i = 0; i < TABLE_SIZE; i++) ht.bucket[i] = NULL; insert(&ht, "C", "A general-purpose programming language."); insert(&ht, "Hash", "A function that converts data into a fixed-size value."); insert(&ht, "Pointer", "A variable that stores a memory address."); printf("\n--- Dictionary Search ---\n"); search(&ht, "C"); search(&ht, "Python"); // Not inserted return 0; Use code with caution. 3. How the Code Works
free(table->buckets); free(table);
printf("contains apple? %s\n", ht_contains(dict, "apple") ? "yes" : "no");
typedef struct Node char *key; int value; struct Node *next; Node; c program to implement dictionary using hashing algorithms
When two unique strings yield the same index, a collision occurs. Instead of searching for the next empty array index (Open Addressing), this code handles collisions via . Each array slot ("bucket") contains a pointer to a singly linked list of Node structures. If a collision happens, the new node is pushed directly to the head of that specific bucket's linked list. Memory Optimization and Management
occurs when all keys hash to the same bucket (poor hash function or malicious input). With a good hash function and reasonable load factor, average remains constant.
#include <stdio.h> #include <stdlib.h> #include <string.h> #include #include #include #define TABLE_SIZE 10 // Node
// Insertions insert(myDict, "apple", 10); insert(myDict, "banana", 20); insert(myDict, "cherry", 30);
#include #include #include #define TABLE_SIZE 17 #define MAX_KEY_LEN 64 #define MAX_VAL_LEN 128 // Node structure for separate chaining typedef struct Node char key[MAX_KEY_LEN]; char value[MAX_VAL_LEN]; struct Node* next; Node; // Dictionary structure containing the hash table typedef struct Node* buckets[TABLE_SIZE]; Dictionary; // 1. The DJB2 Hashing Algorithm unsigned long hash_function(const char* str) unsigned long hash = 5381; int c; while ((c = *str++)) hash = ((hash << 5) + hash) + c; // hash * 33 + c return hash % TABLE_SIZE; // Initialize the dictionary buckets to NULL void init_dictionary(Dictionary* dict) for (int i = 0; i < TABLE_SIZE; i++) dict->buckets[i] = NULL; // 2. Insert or Update a Key-Value Pair int insert(Dictionary* dict, const char* key, const char* value) unsigned long index = hash_function(key); Node* current = dict->buckets[index]; // Check if the key already exists to update its value while (current != NULL) if (strcmp(current->key, key) == 0) strncpy(current->value, value, MAX_VAL_LEN - 1); current->value[MAX_VAL_LEN - 1] = '\0'; return 1; // Updated existing key current = current->next; // Key does not exist; create a new node Node* new_node = (Node*)malloc(sizeof(Node)); if (!new_node) return 0; // Memory allocation failed strncpy(new_node->key, key, MAX_KEY_LEN - 1); new_node->key[MAX_KEY_LEN - 1] = '\0'; strncpy(new_node->value, value, MAX_VAL_LEN - 1); new_node->value[MAX_VAL_LEN - 1] = '\0'; // Insert at the head of the linked list (O(1) insertion) new_node->next = dict->buckets[index]; dict->buckets[index] = new_node; return 2; // Inserted new key // 3. Search for a Key const char* search(Dictionary* dict, const char* key) unsigned long index = hash_function(key); Node* current = dict->buckets[index]; while (current != NULL) if (strcmp(current->key, key) == 0) return current->value; // Key found current = current->next; return NULL; // Key not found // 4. Delete a Key-Value Pair int delete(Dictionary* dict, const char* key) unsigned long index = hash_function(key); Node* current = dict->buckets[index]; Node* prev = NULL; while (current != NULL) if (strcmp(current->key, key) == 0) if (prev == NULL) // Node to delete is the head of the bucket list dict->buckets[index] = current->next; else // Node to delete is in the middle or end prev->next = current->next; free(current); return 1; // Successfully deleted prev = current; current = current->next; return 0; // Key not found // 5. Display the Hash Table Structure void display_dictionary(Dictionary* dict) printf("\n--- Dictionary Hash Table Structure ---\n"); for (int i = 0; i < TABLE_SIZE; i++) printf("Bucket [%d]: ", i); Node* current = dict->buckets[i]; if (current == NULL) printf("NULL\n"); continue; while (current != NULL) printf("[%s: %s] -> ", current->key, current->value); current = current->next; printf("NULL\n"); printf("----------------------------------------\n"); // 6. Free Allocated Memory void free_dictionary(Dictionary* dict) for (int i = 0; i < TABLE_SIZE; i++) Node* current = dict->buckets[i]; while (current != NULL) Node* temp = current; current = current->next; free(temp); dict->buckets[i] = NULL; // Demonstration int main() Dictionary my_dict; init_dictionary(&my_dict); // Inserting values insert(&my_dict, "Alice", "Engineer"); insert(&my_dict, "Bob", "Designer"); insert(&my_dict, "Charlie", "Manager"); insert(&my_dict, "David", "Developer"); // Potential collision demo // Displaying initial table display_dictionary(&my_dict); // Searching test const char* target = "Bob"; const char* role = search(&my_dict, target); if (role) printf("\nFound! %s is a %s.\n", target, role); else printf("\n%s not found.\n", target); // Updating an existing key printf("\nUpdating Alice's role...\n"); insert(&my_dict, "Alice", "Director"); // Deleting a key printf("\nDeleting Charlie...\n"); delete(&my_dict, "Charlie"); // Displaying final table display_dictionary(&my_dict); // Clean up memory free_dictionary(&my_dict); return 0; Use code with caution. Detailed Code Walkthrough The DJB2 Hashing Function
printf("Value of apple: %s\n", get(dict, "apple")); printf("Value of banana: %s\n", get(dict, "banana")); printf("Value of carrot: %s\n", get(dict, "carrot")); Instead of searching for the next empty array
For debugging and demonstration, we can print all key‑value pairs:
A hash function mathematically maps arbitrary data (like a string) to a fixed-size integer, which corresponds to an index in the hash table array. A good hash function should:
// --- Hash Function ---