Collision Handling in Hashing
Overview
In hashing, collision handling refers to the methods used to resolve the issue when two keys hash to the same index in a hash table. Collisions are inevitable in hash tables due to the limited number of available slots compared to the potentially infinite number of input keys.
Methods of Collision Handling
1. Chaining (Open Hashing)
- Chaining stores multiple elements in the same bucket using a data structure like a linked list.
- When a collision occurs, the new key-value pair is added to the end of the linked list at that index.
- Advantages: Simple to implement, no need to resize the hash table.
- Disadvantages: Requires extra memory for pointers, performance degrades if many elements collide at the same index.
2. Open Addressing (Closed Hashing)
- In open addressing, all elements are stored within the hash table itself, and when a collision occurs, alternative slots are probed to find an empty one.
- Common probing techniques include:
- Linear Probing: Increment the index sequentially until an empty slot is found.
- Quadratic Probing: Probe at quadratic intervals (i.e., 1, 4, 9, ...).
- Double Hashing: Use a second hash function to determine the probe step.
- Advantages: No need for extra memory for pointers, can be efficient for smaller tables.
- Disadvantages: Can lead to clustering (for linear probing), needs good probing strategies to avoid performance degradation.
Example: Chaining in Python
class HashTable:
def __init__(self):
self.table = [[] for _ in range(10)] # Hash table with 10 slots
def insert(self, key, value):
index = hash(key) % len(self.table)
for pair in self.table[index]:
if pair[0] == key:
pair[1] = value # Update if key exists
return
self.table[index].append([key, value]) # Add new key-value pair
def search(self, key):
index = hash(key) % len(self.table)
for pair in self.table[index]:
if pair[0] == key:
return pair[1]
return None
def delete(self, key):
index = hash(key) % len(self.table)
for i, pair in enumerate(self.table[index]):
if pair[0] == key:
del self.table[index][i]
return
# Example usage
hash_table = HashTable()
hash_table.insert('apple', 1)
hash_table.insert('banana', 2)
hash_table.insert('orange', 3)
print(hash_table.search('banana')) # Output: 2
hash_table.delete('banana')
print(hash_table.search('banana')) # Output: None
Example: Chaining in JavaScript
class HashTable {
constructor() {
this.table = Array.from({ length: 10 }, () => []); // Hash table with 10 slots
}
hash(key) {
let hash = 0;
for (let char of key.toString()) {
hash += char.charCodeAt(0);
}
return hash % this.table.length; // Simple hash function
}
insert(key, value) {
const index = this.hash(key);
for (let pair of this.table[index]) {
if (pair[0] === key) {
pair[1] = value; // Update if key exists
return;
}
}
this.table[index].push([key, value]); // Add new key-value pair
}
search(key) {
const index = this.hash(key);
for (let pair of this.table[index]) {
if (pair[0] === key) {
return pair[1]; // Return the value if key is found
}
}
return null; // Key not found
}
delete(key) {
const index = this.hash(key);
this.table[index] = this.table[index].filter(pair => pair[0] !== key); // Remove the key-value pair
}
}
// Example usage
const hashTable = new HashTable();
hashTable.insert('apple', 1);
hashTable.insert('banana', 2);
hashTable.insert('orange', 3);
console.log(hashTable.search('banana')); // Output: 2
hashTable.delete('banana');
console.log(hashTable.search('banana')); // Output: null