C Program To Implement Dictionary Using Hashing Algorithms May 2026
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// --- Configuration ---
#define TABLE_SIZE 100
// --- Data Structures ---
// 1. The Key-Value Pair Node
typedef struct KeyValue
char *key;
int value;
struct KeyValue *next; // For collision chaining
KeyValue;
// 2. The Dictionary Structure
typedef struct Dictionary
KeyValue **table; // Array of pointers to KeyValue nodes
int size;
int count; // Number of elements currently stored
Dictionary;
// --- Hash Function ---
// djb2 Hash Function
unsigned long hash(const char *str)
unsigned long hash = 5381;
int c;
while ((c = *str++))
hash = ((hash << 5) + hash) + c; // hash * 33 + c
return hash % TABLE_SIZE;
// --- Memory Helpers ---
// Helper to duplicate string (C99 standard has strdup, but this is portable)
char* duplicate_string(const char *src)
char *dst = malloc(strlen(src) + 1);
if (dst) strcpy(dst, src);
return dst;
// --- Dictionary Operations ---
// 1. Initialization
Dictionary* create_dictionary()
Dictionary *dict = (Dictionary*)malloc(sizeof(Dictionary));
dict->size = TABLE_SIZE;
dict->count = 0;
// Allocate memory for the array of pointers and initialize to NULL
dict->table = (KeyValue**)calloc(TABLE_SIZE, sizeof(KeyValue*));
return dict;
// 2. Insertion / Update
void insert(Dictionary *dict, const char *key, int value)
unsigned long index = hash(key);
KeyValue *current = dict->table[index];
// Case A: Key exists? Update the value.
while (current != NULL)
if (strcmp(current->key, key) == 0)
current->value = value;
return;
current = current->next;
// Case B: Key does not exist. Create a new node.
KeyValue *new_node = (KeyValue*)malloc(sizeof(KeyValue));
new_node->key = duplicate_string(key);
new_node->value = value;
// Insert at the head of the linked list (O(1) prepend)
new_node->next = dict->table[index];
dict->table[index] = new_node;
dict->count++;
// 3. Search / Lookup
// Returns pointer to value so we can check for NULL if key not found
int* get(Dictionary *dict, const char *key)
unsigned long index = hash(key);
KeyValue *current = dict->table[index];
while (current != NULL)
if (strcmp(current->key, key) == 0)
return &(current->value); // Return address of value
current = current->next;
return NULL; // Key not found
// 4. Deletion
void delete_key(Dictionary *dict, const char *key)
unsigned long index = hash(key);
KeyValue *current = dict->table[index];
KeyValue *prev = NULL;
while (current != NULL)
if (strcmp(current->key, key) == 0)
if (prev == NULL)
// Node is at the head
dict->table[index] = current->next;
else
// Node is in the middle or end
prev->next = current->next;
free(current->key);
free(current);
dict->count--;
return;
prev = current;
current = current->next;
printf("Key '%s' not found.\n", key);
// 5. Cleanup
void free_dictionary(Dictionary *dict)
for (int i = 0; i < dict->size; i++)
KeyValue *current = dict->table[i];
while (current != NULL)
KeyValue *temp = current;
current = current->next;
free(temp->key);
free(temp);
free(dict->table);
free(dict);
// 6. Utility: Print Dictionary
void print_dictionary(Dictionary *dict)
printf("\n--- Dictionary Contents ---\n");
for (int i = 0; i < dict->size; i++)
KeyValue *current = dict->table[i];
if (current != NULL)
printf("Index [%d]: ", i);
while (current != NULL)
printf("(%s: %d) -> ", current->key, current->value);
current = current->next;
printf("NULL\n");
printf("---------------------------\n");
// --- Main Driver ---
int main()
Dictionary *myDict = create_dictionary();
// Insertions
insert(myDict, "apple", 10);
insert(myDict, "banana", 20);
insert(myDict, "cherry", 30);
// Update
insert(myDict, "apple", 15); // Update apple's value
// Lookup
int *val = get(myDict, "banana");
if (val) printf("Get 'banana': %d\n", *val);
val = get(myDict, "grape");
if (!val) printf("Get 'grape': Key not found.\n");
// Print all to see structure
print_dictionary(myDict);
// Deletion
printf("Deleting 'banana'...\n");
delete_key(myDict, "banana");
print_dictionary(myDict);
// Cleanup
free_dictionary(myDict);
return 0;
int get(Dictionary* dict, const char* key, int* found)
int index = hash(key, dict->size);
Entry* curr = dict->buckets[index];
while (curr != NULL)
if (strcmp(curr->key, key) == 0)
*found = 1;
return curr->value;
curr = curr->next;
*found = 0;
return -1;
// Search: returns the value if key exists, or -1 if not found
// (In production, use a status flag or pointer to indicate failure)
int search(HashTable *table, const char *key, int *found)
if (!table
Excellent for low collision rates.
unsigned long hash_sdbm(const char *str)
unsigned long hash = 0;
int c;
while ((c = *str++))
hash = c + (hash << 6) + (hash << 16) - hash;
return hash;
The hash function converts a string key into an index. A good hash function:
Simple polynomial rolling hash (djb2 variant):
unsigned long hash(const char *str, int table_size) unsigned long hash = 5381; int c;while ((c = *str++)) hash = ((hash << 5) + hash) + c; // hash * 33 + c return hash % table_size;
Alternative: FNV-1a hash (better distribution):
unsigned long hash_fnv1a(const char *str, int table_size) unsigned long hash = 2166136261UL; int c;while ((c = *str++)) hash ^= c; hash *= 16777619; return hash % table_size;
Below is a complete, compilable C program. It implements a Dictionary with String keys and Integer values.
While separate chaining is robust, open addressing saves memory by storing entries directly in the array. Here’s a simplified version using linear probing.
typedef struct char *key; char *value; int is_occupied; // Tombstone support for deletions HashEntry;typedef struct HashEntry *entries; unsigned long size; unsigned long count; ProbeDict;
unsigned long find_slot(ProbeDict *dict, const char *key) unsigned long hash = hash_djb2(key); unsigned long index = hash % dict->size; unsigned long original = index; c program to implement dictionary using hashing algorithms
while (dict->entries[index].is_occupied) if (dict->entries[index].key && strcmp(dict->entries[index].key, key) == 0) return index; // Found index = (index + 1) % dict->size; if (index == original) break; // Full table return index; // Empty slot or tombstone
Pros of probing: Better cache locality.
Cons: Clustering issues, more complex deletions (require tombstones).
We need to handle:
void insert(Dictionary *dict, const char *key, const char *value) unsigned long hash = dict->hash_func(key); unsigned long index = hash % dict->size;// Check if key already exists Entry *curr = dict->buckets[index]; while (curr) if (strcmp(curr->key, key) == 0) // Update existing value free(curr->value); curr->value = strdup(value); return; curr = curr->next; // Create new entry Entry *new_entry = (Entry*)malloc(sizeof(Entry)); new_entry->key = strdup(key); new_entry->value = strdup(value); new_entry->next = dict->buckets[index]; dict->buckets[index] = new_entry; dict->count++; // Check load factor and resize if needed if ((float)dict->count / dict->size > LOAD_FACTOR_THRESHOLD) resize_dictionary(dict);