Step 1 of 5
20% CompleteIntroduction to Hash Tables
Learn the fundamentals of hash tables and how they work
What is a Hash Table?
A hash table (also known as a hash map) is a data structure that implements an associative array abstract data type, a structure that can map keys to values. It uses a hash function to compute an index into an array of buckets or slots, from which the desired value can be found.
Hash tables are designed to be extremely efficient for lookup, insertion, and deletion operations, typically achieving O(1) time complexity on average.
Key Components
Hash Function
Converts keys into array indices. A good hash function distributes keys uniformly across the array to minimize collisions.
Buckets/Slots
The array elements where key-value pairs are stored. Each bucket can hold a single entry or multiple entries (in case of collisions).
Collision Resolution
Strategies for handling situations when two different keys hash to the same index.
Load Factor
The ratio of the number of stored entries to the number of buckets, which affects performance and memory usage.
How Hash Tables Work
When you want to store a key-value pair in a hash table:
- The hash function computes a hash code for the key
- The hash code is mapped to an index in the array
- The key-value pair is stored at that index
When you want to retrieve a value by its key:
- The hash function computes the same hash code for the key
- The hash code is mapped to the same index in the array
- The value is retrieved from that index
Collision Handling
Collisions occur when two different keys hash to the same index. There are several strategies to handle collisions:
Separate Chaining
Each bucket contains a linked list of entries. When a collision occurs, the new entry is appended to the list.
// Simplified separate chaining implementationclass HashTable {constructor(size = 53) {this.keyMap = Array(size).fill().map(() => []);}_hash(key) {// Simple hash functionlet total = 0;const PRIME = 31;for (let i = 0; i < Math.min(key.length, 100); i++) {const char = key[i];const value = char.charCodeAt(0) - 96;total = (total * PRIME + value) % this.keyMap.length;}return total;}}
Open Addressing
When a collision occurs, the algorithm searches for the next available slot in the array. Common probing techniques include:
- Linear Probing: Check the next slot sequentially
- Quadratic Probing: Check slots at quadratic distances
- Double Hashing: Use a second hash function to determine the step size
Basic Hash Table Implementation
Here's a simple implementation of a hash table using separate chaining:
class HashTable {constructor(size = 53) {this.keyMap = Array(size).fill().map(() => []);}_hash(key) {let total = 0;const PRIME = 31;for (let i = 0; i < Math.min(key.length, 100); i++) {const char = key[i];const value = char.charCodeAt(0) - 96;total = (total * PRIME + value) % this.keyMap.length;}return total;}set(key, value) {const index = this._hash(key);this.keyMap[index].push([key, value]);return index;}get(key) {const index = this._hash(key);for (let i = 0; i < this.keyMap[index].length; i++) {if (this.keyMap[index][i][0] === key) {return this.keyMap[index][i][1];}}return undefined;}}
Time Complexity
Hash tables offer excellent performance characteristics:
Operation | Average Case | Worst Case |
---|---|---|
Insert | O(1) | O(n) |
Delete | O(1) | O(n) |
Search | O(1) | O(n) |
Note: The worst-case O(n) occurs when all keys hash to the same index, creating a long chain. With a good hash function and appropriate load factor, this is extremely rare.
Next Steps
In the next sections, we'll explore the core operations of hash tables in detail:
- Insert Operation: Adding key-value pairs to the hash table
- Delete Operation: Removing entries from the hash table
- Search Operation: Finding values by their keys