Hash Tables — O(1) Lookup
Database, cache, compiler-এর মেরুদণ্ড
1. The Dream: O(1) Access by Any Key
Array integer index-এ O(1); hash table যেকোনো key-এ (string, struct, etc.) O(1) গড় — key-কে একটি integer index-এ hash function মাধ্যমে রূপান্তর করে।
2. A Simple String Hash — djb2
#include <stdio.h>
unsigned long hash_djb2(const char *s) {
unsigned long h = 5381;
int c;
while ((c = *s++)) h = ((h << 5) + h) + c; // h * 33 + c
return h;
}
int main(void) {
const char *keys[] = { "apple", "banana", "mango", "apple" };
for (int i = 0; i < 4; i++)
printf("%-8s → %lu → bucket %lu\n",
keys[i], hash_djb2(keys[i]), hash_djb2(keys[i]) % 16);
return 0;
}
Identical key সবসময় identical hash দেবে। Modulo দিয়ে bucket index।
3. Collisions — Two Strategies
- Separate chaining — প্রতিটি bucket একটি linked list।
- Open addressing — collision-এ পরের slot দেখা (linear/quadratic probing, double hashing)।
4. Hash Map with Separate Chaining — Full Implementation
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct Entry {
char *key;
int val;
struct Entry *next;
} Entry;
#define CAP 16
static Entry *buckets[CAP];
static unsigned long h(const char *s) {
unsigned long v = 5381; int c;
while ((c = *s++)) v = ((v << 5) + v) + c;
return v;
}
void map_put(const char *k, int v) {
unsigned long i = h(k) % CAP;
for (Entry *e = buckets[i]; e; e = e->next)
if (strcmp(e->key, k) == 0) { e->val = v; return; }
Entry *e = malloc(sizeof *e);
e->key = strdup(k); e->val = v;
e->next = buckets[i];
buckets[i] = e;
}
int map_get(const char *k, int *out) {
unsigned long i = h(k) % CAP;
for (Entry *e = buckets[i]; e; e = e->next)
if (strcmp(e->key, k) == 0) { *out = e->val; return 1; }
return 0;
}
void map_free(void) {
for (int i = 0; i < CAP; i++) {
Entry *e = buckets[i];
while (e) { Entry *t = e; e = e->next; free(t->key); free(t); }
buckets[i] = NULL;
}
}
int main(void) {
map_put("apple", 10);
map_put("banana", 5);
map_put("mango", 20);
map_put("apple", 42); // update
int v;
if (map_get("apple", &v)) printf("apple = %d\n", v);
if (map_get("banana", &v)) printf("banana = %d\n", v);
if (!map_get("cherry", &v)) puts("cherry: not found");
map_free();
return 0;
}
5. Load Factor & Rehashing
Load factor = n / capacity। 0.75-এর বেশি হলে capacity দ্বিগুণ করে সব entry নতুন table-এ আবার insert করুন (rehash)। Insert গড় O(1) থাকে — amortized।
6. Where Hash Tables Shine
- Word frequency counters
- Caches (LRU + map)
- Compiler symbol tables
- Database indexes
- Set membership (just keys, no values)
7. Practice Problems
- Implement
map_put,map_get,map_free(full chaining hash map).Full chaining hash map লিখুন।✨ Show Answer
Section 4-এর
hashmap.c-ই সম্পূর্ণ উত্তর। - Implement
map_delete(key).map_deleteযোগ করুন।✨ Show Answer
int map_delete(const char *k) { unsigned long i = h(k) % CAP; Entry **p = &buckets[i]; while (*p) { if (strcmp((*p)->key, k) == 0) { Entry *t = *p; *p = t->next; free(t->key); free(t); return 1; } p = &(*p)->next; } return 0; } - Count word frequencies in a file.ফাইলে শব্দের frequency গুনুন।
✨ Show Answer
File থেকে word-by-word পড়ে map-এ increment —
int v; if (map_get(w, &v)) map_put(w, v+1); else map_put(w, 1); - First non-repeating character in a string (count map).String-এ প্রথম non-repeating character।
✨ Show Answer
প্রথম pass-এ frequency বসান, দ্বিতীয় pass-এ প্রথম যে character-এর count 1 সেটাই উত্তর।
- Two-sum — find two indices summing to target in O(n) with hash set.Hash দিয়ে O(n)-এ two-sum।
✨ Show Answer
Iterate করুন: প্রতিটি
a[i]-র জন্যtarget - a[i]map-এ আছে কি না দেখুন; পেলেই return। না পেলেa[i] → imap-এ রাখুন। - Group anagrams.Anagram একসাথে রাখুন।
✨ Show Answer
প্রতিটি word-এর characters sort করে সেটিকে key হিসেবে map-এ group করুন। একই sorted key = anagram group।
- Longest substring without repeating characters.Longest substring without repeat।
✨ Show Answer
Sliding window + map (char → last index)। Repeat এলে left-কে সেই index-এর পরে টানুন।
- Subarray with sum = K (negatives allowed).Subarray with sum K।
✨ Show Answer
Prefix-sum count map।
sum - kযতবার দেখা গেছে তত valid subarray। O(n)। - LRU cache using hash map + doubly linked list.Hash + doubly list দিয়ে LRU।
✨ Show Answer
Map = key → node pointer। List = MRU...LRU। Get: list-এ node সামনে আনুন। Put: evict লাগলে list-এর tail remove, map থেকে delete।
- Open addressing (linear probing) hash table.Linear probing hash table।
✨ Show Answer
Array-এ slot state: EMPTY/FILLED/TOMBSTONE। Insert: empty বা tombstone না পাওয়া পর্যন্ত (i+1)%CAP। Delete: slot-কে TOMBSTONE করুন।
- Benchmark linear probing vs chaining.Linear probing vs chaining benchmark।
✨ Show Answer
ছোট load factor-এ (≤ 0.5) open addressing cache-friendly বলে দ্রুত। Load factor ~0.75+ হলে chaining ভালো। Benchmark-এ 10⁶ key insert+lookup করুন।
- Why does a poor hash function destroy performance? Demonstrate.দুর্বল hash function performance-এ কী ক্ষতি করে?
✨ Show Answer
সবাই একই bucket-এ পড়লে hash table একটি linked list-এ পরিণত হয় — lookup O(n)। দুর্বল hash-এর উদাহরণ:
return key[0]— সব একই অক্ষরের শুরু-key একই bucket-এ। - Resize (rehash) when load factor > 0.75.Load factor 0.75 ছাড়ালে rehash করুন।
✨ Show Answer
নতুন capacity (দ্বিগুণ) দিয়ে নতুন buckets array; পুরনো প্রতিটি entry নতুন hash % new_cap-এ insert করুন; পুরনো free করুন।
- Why does modulo by a prime help weak hashes?Prime modulo দুর্বল hash-এ কেন সাহায্য করে?
✨ Show Answer
Non-prime (বিশেষত 2-এর power) divisor-এ hash value-র নিম্ন bit গুলোই bucket নির্ধারণ করে — অনেক key একই bucket-এ পড়ে। Prime modulo সমস্ত bit-কে mix করে, ভাল distribution দেয়।
- Implement a simple Bloom filter.Simple Bloom filter বানান।
✨ Show Answer
k-টি hash function + bit array। Insert: সব k-bit set। Test: সব k-bit 1 হলে probably present, কোনোটা 0 হলে definitely not। False positive থাকে, false negative নেই।
- Compute expected collisions for n keys in m buckets (birthday paradox).n key, m bucket-এ expected collision।
✨ Show Answer
Expected collision-free probability ≈ e-n²/(2m)। n ≈ √m হলে ~50% collision — এজন্যই একটি 365-দিনের বছরে মাত্র 23 জন মানুষের মধ্যে একই birthday-র probability 50%।
- Implement a set (no values) using chaining.Value ছাড়া শুধু key-এর set।
✨ Show Answer
Entry struct থেকে
valফেলে দিন।set_add,set_has,set_remove— map-এর মতোই। - Compare: Python dict vs your C hash map — what's different?Python dict ও আপনার C hash map-এর পার্থক্য।
✨ Show Answer
Python dict — open addressing (perturbation probe), insertion order preserved (3.7+), resizing at ~2/3, randomized hash (security)। C-তে আপনি এগুলো নিজে হাতে সাজান; trade-off বুঝে design করুন।
Glossary (শব্দকোষ)
| Term | Meaning | বাংলায় |
|---|---|---|
| Hash Table | Key-value structure with average O(1) access. | গড়ে O(1) access-যুক্ত key-value structure। |
| Hash Function | Maps a key to an integer index. | Key-কে integer index-এ মাপ করে। |
| Bucket / Slot | One entry in the hash table array. | Hash table array-এর একটি entry। |
| Collision | When two keys hash to the same bucket. | দুটি key একই bucket-এ পড়ে যাওয়া। |
| Chaining | Each bucket holds a linked list of colliding entries. | প্রতি bucket-এ collision-যুক্ত entry-র list। |
| Open Addressing | On collision, probe other buckets in the array itself. | Collision-এ অন্য bucket খুঁজে বসানো। |
| Linear Probing | Try buckets h+1, h+2... on collision. | Collision-এ পরের পরের bucket চেষ্টা করা। |
| Quadratic Probing | Probe by h+1², h+2² to spread collisions. | Collision ছড়াতে quadratic probe। |
| Double Hashing | Probe step computed by a second hash function. | দ্বিতীয় hash দিয়ে probe step। |
| Load Factor | Ratio of entries to buckets — drives rehash. | Entry/bucket ratio — rehash-এর সংকেত। |
| Rehashing | Resizing the table and re-inserting all entries. | Table বড় করে সব entry আবার বসানো। |
| Tombstone | Marker for deleted slot in open addressing. | Open addressing-এ delete-হওয়া slot-এর চিহ্ন। |
| Uniform Hashing | Ideal hash that distributes keys evenly. | Key সমানভাবে ছড়ানো hash। |
Summary — Module 28
ভালো hash + collision strategy = O(1) গড় access। সরল জন্য chaining, cache efficiency-র জন্য open addressing। Load factor বাড়লে rehash করুন। Hash function key-গুলো ভালোভাবে spread করা দরকার।