Hash Table
Data Structure · Core
Step through it: Sliding Window pattern
Window "a", length 1
best so far: 1
Step 1 of 6
Goal: find the longest stretch of letters with no letter repeated. Checking every stretch is slow. Instead keep one window over the string and only ever slide it forward.
Step 2 of 6
Grow the window to the right while each new letter is different. "abc" has no repeats, so its length, 3, is the best so far.
Step 3 of 6
The next letter is "a", but the window already has an "a". The window is no longer valid.
Step 4 of 6
Fix it by shrinking from the left until the repeat is gone. Dropping the first "a" leaves "bca", valid again.
Step 5 of 6
Keep growing: add "d", "e", "f". The best is now "bcadef", length 6. The left edge and right edge only ever move forward, so each letter is added once and dropped at most once. This is called a sliding window.
Step 6 of 6
Your turn. Pick a word and press Next to watch the window grow and shrink.
In computing, a hash table (hash map) is a data structure which implements an associative array abstract data type, a structure that can map keys to values. A hash table uses a hash function to compute an index into an array of buckets or slots, from which the desired value can be found
Ideally, the hash function will assign each key to a unique bucket, but most hash table designs employ an imperfect hash function, which might cause hash collisions where the hash function generates the same index for more than one key. Such collisions must be accommodated in some way.

Hash collision resolved by separate chaining.

Made with okso.app
Code
import LinkedList from '../linked-list/LinkedList';
// Hash table size directly affects on the number of collisions.
// The bigger the hash table size the less collisions you'll get.
// For demonstrating purposes hash table size is small to show how collisions
// are being handled.
const defaultHashTableSize = 32;
export default class HashTable {
/**
* @param {number} hashTableSize
*/
constructor(hashTableSize = defaultHashTableSize) {
// Create hash table of certain size and fill each bucket with empty linked list.
this.buckets = Array(hashTableSize).fill(null).map(() => new LinkedList());
// Just to keep track of all actual keys in a fast way.
this.keys = {};
}
/**
* Converts key string to hash number.
*
* @param {string} key
* @return {number}
*/
hash(key) {
// For simplicity reasons we will just use character codes sum of all characters of the key
// to calculate the hash.
//
// But you may also use more sophisticated approaches like polynomial string hash to reduce the
// number of collisions:
//
// hash = charCodeAt(0) * PRIME^(n-1) + charCodeAt(1) * PRIME^(n-2) + ... + charCodeAt(n-1)
//
// where charCodeAt(i) is the i-th character code of the key, n is the length of the key and
// PRIME is just any prime number like 31.
const hash = Array.from(key).reduce(
(hashAccumulator, keySymbol) => (hashAccumulator + keySymbol.charCodeAt(0)),
0,
);
// Reduce hash number so it would fit hash table size.
return hash % this.buckets.length;
}
/**
* @param {string} key
* @param {*} value
*/
set(key, value) {
const keyHash = this.hash(key);
this.keys[key] = keyHash;
const bucketLinkedList = this.buckets[keyHash];
const node = bucketLinkedList.find({ callback: (nodeValue) => nodeValue.key === key });
if (!node) {
// Insert new node.
bucketLinkedList.append({ key, value });
} else {
// Update value of existing node.
node.value.value = value;
}
}
/**
* @param {string} key
* @return {*}
*/
delete(key) {
const keyHash = this.hash(key);
delete this.keys[key];
const bucketLinkedList = this.buckets[keyHash];
const node = bucketLinkedList.find({ callback: (nodeValue) => nodeValue.key === key });
if (node) {
return bucketLinkedList.delete(node.value);
}
return null;
}
/**
* @param {string} key
* @return {*}
*/
get(key) {
const bucketLinkedList = this.buckets[this.hash(key)];
const node = bucketLinkedList.find({ callback: (nodeValue) => nodeValue.key === key });
return node ? node.value.value : undefined;
}
/**
* @param {string} key
* @return {boolean}
*/
has(key) {
return Object.hasOwnProperty.call(this.keys, key);
}
/**
* @return {string[]}
*/
getKeys() {
return Object.keys(this.keys);
}
/**
* Gets the list of all the stored values in the hash table.
*
* @return {*[]}
*/
getValues() {
return this.buckets.reduce((values, bucket) => {
const bucketValues = bucket.toArray()
.map((linkedListNode) => linkedListNode.value.value);
return values.concat(bucketValues);
}, []);
}
}References
Practice
Sliding Window
- Best Time to Buy and Sell StockEasy
- Maximum Average Subarray IEasy
- Contains Duplicate IIEasy
- Longest Nice SubarrayEasy
- Longest Substring Without Repeating CharactersMedium
- Longest Repeating Character ReplacementMedium
- Permutation in StringMedium
- Find All Anagrams in a StringMedium
- Minimum Size Subarray SumMedium
- Max Consecutive Ones IIIMedium
- Fruit Into BasketsMedium
- Longest Substring with At Most K Distinct CharactersMedium
- Subarray Product Less Than KMedium
- Maximum Points You Can Obtain from CardsMedium
- Count Number of Nice SubarraysMedium
- Minimum Window SubstringHard
- Sliding Window MaximumHard
- Substring with Concatenation of All WordsHard
Hash Map / Set Lookup
- Two SumEasy
- Contains DuplicateEasy
- Valid AnagramEasy
- Ransom NoteEasy
- Isomorphic StringsEasy
- Intersection of Two ArraysEasy
- Majority ElementEasy
- Single NumberEasy
- Group AnagramsMedium
- Longest Consecutive SequenceMedium
- LRU CacheMedium
- Insert Delete GetRandom O(1)Medium
- Encode and Decode TinyURLMedium
- 4Sum IIMedium
- Brick WallMedium
- Custom Sort StringMedium
- LFU CacheHard
- All O`one Data StructureHard
Spotted a mistake or something unclear? Report it on GitHub.