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);