Read Key
Accept an integer, string or record key.
Code
Bhavya
Convert keys into table locations and master collisions using separate chaining, linear probing, quadratic probing, double hashing, tombstones and dynamic rehashing.
A hash table stores a key in a bucket selected by a hash function. Instead of comparing against many sorted elements, it calculates where the key should be and examines only that location or a short collision path.
Accept an integer, string or record key.
Transform the key into an integer hash code.
Compress the hash code into the table range.
Use a chain or a probe sequence when occupied.
A table location that stores an entry or begins a chain.
Different keys map to the same initial bucket.
ฮฑ = n/m, where n is keys and m is buckets.
O(1) with good distribution and controlled load.
| Method | Formula or idea | Important note |
|---|---|---|
| Division | h(k) = k mod m | Choose m carefully, commonly a prime. |
| Multiplication | โm(kA mod 1)โ | Less sensitive to the table size. |
| Mid-square | Square the key and take middle digits | Middle digits depend on several input digits. |
| Folding | Split the key and add its parts | Useful for long numeric identifiers. |
| Polynomial string | h = (h ร p + character) mod m | Character order affects the result. |
| Universal | Randomly choose from a hash family | Limits adversarial collision patterns. |
Fast, deterministic, uses the whole key and spreads expected inputs uniformly.
Ignores important key variation or repeatedly maps common inputs into a few buckets.
When the key universe is larger than the table, collisions are unavoidable. Correct tables preserve every colliding key and still follow a reproducible search path.
Every bucket owns a linked list or another secondary container. Colliding keys stay together.
All keys stay inside the array. A probe formula generates alternative buckets.
(h(k) + i) mod m
(h(k) + cโi + cโiยฒ) mod m
(hโ(k) + i ร hโ(k)) mod m
Search repeats the exact insertion sequence.
Separate chaining stores each entry in the linked list selected by h(key). Its load factor may exceed 1 because the number of records is not limited by the bucket count.
#include <stdio.h>
#include <stdlib.h>
#define SIZE 11
typedef struct Node {
int key;
struct Node *next;
} Node;
Node *table[SIZE];
int hashKey(int key) {
int value = key % SIZE; /* hash compute */
return value < 0 ? value + SIZE : value;
}
void initialize(void) {
for (int i = 0; i < SIZE; i++)
table[i] = NULL; /* chain create table */
}
Node *searchKey(int key) {
int index = hashKey(key); /* chain search hash */
Node *current = table[index];
while (current != NULL) { /* chain search loop */
if (current->key == key)
return current; /* chain search found */
current = current->next;
}
return NULL; /* chain search miss */
}
int insertKey(int key) {
int index = hashKey(key); /* chain insert hash */
for (Node *current = table[index];
current != NULL;
current = current->next) {
/* chain insert check */
if (current->key == key)
return 0;
}
Node *node = malloc(sizeof(Node));
if (node == NULL)
exit(EXIT_FAILURE);
node->key = key;
node->next = table[index];
table[index] = node; /* chain insert link */
return 1;
}
int deleteKey(int key) {
int index = hashKey(key); /* chain delete hash */
Node *current = table[index];
Node *previous = NULL;
while (current != NULL) { /* chain delete loop */
if (current->key == key) {
if (previous == NULL)
table[index] = current->next;
else
previous->next = current->next;
free(current); /* chain delete remove */
return 1;
}
previous = current;
current = current->next;
}
return 0; /* chain delete miss */
}
void display(void) {
for (int i = 0; i < SIZE; i++) {
printf("%2d:", i);
for (Node *current = table[i];
current != NULL;
current = current->next) {
printf(" -> %d", current->key);
}
printf("\n");
}
}
void destroy(void) {
for (int i = 0; i < SIZE; i++) {
Node *current = table[i];
while (current != NULL) {
Node *next = current->next;
free(current);
current = next;
}
}
}
int main(void) {
int values[] = {
27, 18, 29, 28, 39, 13, 16
};
int count =
sizeof(values) / sizeof(values[0]);
initialize();
for (int i = 0; i < count; i++)
insertKey(values[i]); /* chain build */
display();
printf(
"Search 39: %s\n",
searchKey(39) ? "Found" : "Not Found"
);
printf(
"Delete 28: %s\n",
deleteKey(28) ? "Deleted" : "Not Found"
);
display();
destroy();
return 0;
}
27 18 29 28 39 13 16
Search: 39
Delete: 28
Search 39: Found
Delete 28: Deleted
Open addressing generates a sequence of candidate buckets. It has good cache locality, but performance drops sharply as the table becomes crowded.
| Method | Probe | Main strength | Main issue |
|---|---|---|---|
| Linear | h + i | Simple and cache-friendly | Primary clustering |
| Quadratic | h + iยฒ | Reduces primary clustering | Secondary clustering; may not visit every bucket |
| Double hashing | hโ + iยทhโ | Best distribution of the three | Needs a valid non-zero step relatively prime to m |
#include <stdio.h>
#define SIZE 11
#define EMPTY 0
#define OCCUPIED 1
#define DELETED 2
typedef enum {
LINEAR,
QUADRATIC,
DOUBLE_HASHING
} Strategy;
typedef struct {
int key;
int state;
} Slot;
Slot table[SIZE];
int positiveMod(int value, int modulus) {
int result = value % modulus;
return result < 0
? result + modulus
: result;
}
int hashOne(int key) {
return positiveMod(
key,
SIZE
); /* hash compute */
}
int hashTwo(int key) {
return 1 + positiveMod(
key,
SIZE - 1
); /* open second hash */
}
int probeIndex(
int key,
int attempt,
Strategy strategy
) {
int first = hashOne(key);
if (strategy == LINEAR)
return (
first + attempt
) % SIZE; /* open linear probe */
if (strategy == QUADRATIC)
return (
first + attempt * attempt
) % SIZE; /* open quadratic probe */
return (
first + attempt * hashTwo(key)
) % SIZE; /* open double probe */
}
void initialize(void) {
for (int i = 0; i < SIZE; i++)
table[i].state =
EMPTY; /* open create table */
}
int searchKey(
int key,
Strategy strategy
) {
for (
int attempt = 0;
attempt < SIZE;
attempt++
) { /* open search loop */
int index = probeIndex(
key,
attempt,
strategy
);
if (table[index].state == EMPTY)
return -1; /* open search empty */
if (
table[index].state == OCCUPIED &&
table[index].key == key
)
return index; /* open search found */
}
return -1; /* open search miss */
}
int insertKey(
int key,
Strategy strategy
) {
int firstDeleted = -1;
for (
int attempt = 0;
attempt < SIZE;
attempt++
) { /* open insert loop */
int index = probeIndex(
key,
attempt,
strategy
);
if (
table[index].state == OCCUPIED &&
table[index].key == key
)
return 0;
if (
table[index].state == DELETED &&
firstDeleted == -1
)
firstDeleted =
index; /* open remember tombstone */
if (table[index].state == EMPTY) {
if (firstDeleted != -1)
index = firstDeleted;
table[index].key = key;
table[index].state =
OCCUPIED; /* open insert store */
return 1;
}
}
if (firstDeleted != -1) {
table[firstDeleted].key = key;
table[firstDeleted].state =
OCCUPIED; /* open insert tombstone */
return 1;
}
return 0; /* open insert full */
}
int deleteKey(
int key,
Strategy strategy
) {
int index =
searchKey(
key,
strategy
); /* open delete search */
if (index == -1)
return 0;
table[index].state =
DELETED; /* open delete tombstone */
return 1;
}
void display(void) {
for (int i = 0; i < SIZE; i++) {
printf("%2d: ", i);
if (table[i].state == OCCUPIED)
printf("%d", table[i].key);
else if (table[i].state == DELETED)
printf("DELETED");
else
printf("EMPTY");
printf("\n");
}
}
int main(void) {
int values[] = {
27, 18, 29, 28, 39, 13, 16
};
int count =
sizeof(values) / sizeof(values[0]);
Strategy strategy =
DOUBLE_HASHING;
initialize();
for (int i = 0; i < count; i++)
insertKey(
values[i],
strategy
); /* open build */
display();
printf(
"Search 39: index %d\n",
searchKey(39, strategy)
);
printf(
"Delete 28: %s\n",
deleteKey(28, strategy)
? "Deleted"
: "Not Found"
);
display();
return 0;
}
Table size: 11
Keys: 27 18 29 28 39 13 16
Strategy: Double Hashing
Search 39: Found
Delete 28: Deleted
Deleted slot remains a tombstone.
No key has ever occupied it; an unsuccessful search may stop.
A key was deleted; search must continue through it.
Remember the first tombstone, but first ensure the key is not later in the probe sequence.
Create a larger table and insert every live key using the new modulus.
Each key has two candidate positions. Insertion may evict and relocate existing keys; lookup checks only two places.
During probing, keys with longer probe distances can take slots from keys with shorter distances.
A static known key set is arranged to guarantee O(1) worst-case lookup without collisions.
A function is randomly chosen from a family to reduce the effect of hostile input patterns.
Distributed keys move only a limited amount when servers join or leave.
A compact probabilistic structure can reject definite absences, but may report false positives.
| Method | Storage | Deletion | Cache locality | Recommended load |
|---|---|---|---|---|
| Separate chaining | Table plus nodes | Direct unlink | Lower | May exceed 1 |
| Linear probing | Array only | Tombstone/backshift | Excellent | Usually below 0.7 |
| Quadratic probing | Array only | Tombstone | Good | Often below 0.5 |
| Double hashing | Array only | Tombstone | Good | Usually below 0.7 |
| Cuckoo hashing | Two candidate tables/locations | Simple lookup removal | Good | Resize on insertion cycle |
Choose a collision strategy and operation, then follow hash calculations, collisions, chain movement, probes, tombstones and rehashing step by step.
Code
Bhavya
Select a strategy and operation. The matching complete C source appears with line numbers, live variables and the evolving hash table.
| Operation | Expected | Worst case | Reason |
|---|---|---|---|
| Search | O(1) | O(n) | Short chain/probe versus all keys colliding |
| Insert | O(1) | O(n) | Available location versus long collision path |
| Delete | O(1) | O(n) | Locate then unlink or mark |
| Rehash | O(n) | O(nยฒ) with pathological insertion | Every live key must be reinserted |
Map identifiers to type, scope and memory information.
Accelerate equality queries and hash joins.
Associate request keys or words with stored values.
Track previously observed items in expected constant time.
Use dedicated slow salted cryptographic password hashesโnot ordinary table hashes.
Consistent hashing distributes keys across changing servers.
Normalize a negative remainder into the range 0 to m โ 1.
A deleted marker does not terminate an open-address search.
Changing m changes every compressed hash; all entries must be reinserted.
Long chains and probe clusters destroy expected constant time.
A zero or non-coprime step can prevent a probe sequence from reaching the table.
Fast non-cryptographic hashes must not protect passwords.