Design HashMap
Implement a hash map from scratch using array buckets with chaining.
5 min read
hash-map design
Problem#
Design a MyHashMap without using any built-in hash table:
put(key, value)— insert/overwrite.get(key)— return value or -1.remove(key)— delete the mapping.
Examples#
put(1, 1); put(2, 2); get(1)→1;get(3)→-1;put(2, 1); get(2)→1;remove(2); get(2)→-1.
Constraints#
0 <= key, value <= 10^6, up to10^4calls.
Hints#
Hint
Array of
B buckets, each a list of (key, value) pairs. Hash with key % B. Approach#
Fixed-size bucket array (1024 works well here). Each bucket is a list<pair<int,int>>. put scans the bucket and updates or appends; get and remove scan and act.
Solution#
class MyHashMap { static const int B = 1024; vector<list<pair<int,int>>> table; int hash(int k) const { return k % B; }public: MyHashMap() : table(B) {}
void put(int key, int value) { auto& bkt = table[hash(key)]; for (auto& p : bkt) if (p.first == key) { p.second = value; return; } bkt.emplace_back(key, value); }
int get(int key) { auto& bkt = table[hash(key)]; for (auto& p : bkt) if (p.first == key) return p.second; return -1; }
void remove(int key) { auto& bkt = table[hash(key)]; for (auto it = bkt.begin(); it != bkt.end(); ++it) { if (it->first == key) { bkt.erase(it); return; } } }};const numBuckets = 1024
type entry struct { key, value int}
type MyHashMap struct { table [][]entry}
func Constructor() MyHashMap { return MyHashMap{table: make([][]entry, numBuckets)}}
func (m *MyHashMap) hash(k int) int { return k % numBuckets }
func (m *MyHashMap) Put(key, value int) { h := m.hash(key) for i := range m.table[h] { if m.table[h][i].key == key { m.table[h][i].value = value return } } m.table[h] = append(m.table[h], entry{key, value})}
func (m *MyHashMap) Get(key int) int { h := m.hash(key) for _, e := range m.table[h] { if e.key == key { return e.value } } return -1}
func (m *MyHashMap) Remove(key int) { h := m.hash(key) for i, e := range m.table[h] { if e.key == key { m.table[h] = append(m.table[h][:i], m.table[h][i+1:]...) return } }}from typing import List, Tuple
class MyHashMap: _B = 1024
def __init__(self): self.table: List[List[List[int]]] = [[] for _ in range(self._B)]
def _hash(self, key: int) -> int: return key % self._B
def put(self, key: int, value: int) -> None: bkt = self.table[self._hash(key)] for pair in bkt: if pair[0] == key: pair[1] = value return bkt.append([key, value])
def get(self, key: int) -> int: bkt = self.table[self._hash(key)] for k, v in bkt: if k == key: return v return -1
def remove(self, key: int) -> None: bkt = self.table[self._hash(key)] for i, (k, _) in enumerate(bkt): if k == key: bkt.pop(i) returnvar MyHashMap = function() { this.B = 1024; this.table = Array.from({ length: this.B }, () => []);};
MyHashMap.prototype._hash = function(key) { return key % this.B;};
MyHashMap.prototype.put = function(key, value) { const bkt = this.table[this._hash(key)]; for (const pair of bkt) { if (pair[0] === key) { pair[1] = value; return; } } bkt.push([key, value]);};
MyHashMap.prototype.get = function(key) { const bkt = this.table[this._hash(key)]; for (const [k, v] of bkt) { if (k === key) return v; } return -1;};
MyHashMap.prototype.remove = function(key) { const bkt = this.table[this._hash(key)]; for (let i = 0; i < bkt.length; i++) { if (bkt[i][0] === key) { bkt.splice(i, 1); return; } }};class MyHashMap { private static final int B = 1024; private LinkedList<int[]>[] table;
public MyHashMap() { table = new LinkedList[B]; for (int i = 0; i < B; i++) table[i] = new LinkedList<>(); }
private int hash(int k) { return k % B; }
public void put(int key, int value) { LinkedList<int[]> bkt = table[hash(key)]; for (int[] p : bkt) { if (p[0] == key) { p[1] = value; return; } } bkt.add(new int[]{key, value}); }
public int get(int key) { LinkedList<int[]> bkt = table[hash(key)]; for (int[] p : bkt) { if (p[0] == key) return p[1]; } return -1; }
public void remove(int key) { LinkedList<int[]> bkt = table[hash(key)]; Iterator<int[]> it = bkt.iterator(); while (it.hasNext()) { if (it.next()[0] == key) { it.remove(); return; } } }}class MyHashMap { private B = 1024; private table: Array<Array<[number, number]>>;
constructor() { this.table = Array.from({ length: this.B }, () => [] as Array<[number, number]>); }
private hash(key: number): number { return key % this.B; }
put(key: number, value: number): void { const bkt = this.table[this.hash(key)]; for (const pair of bkt) { if (pair[0] === key) { pair[1] = value; return; } } bkt.push([key, value]); }
get(key: number): number { const bkt = this.table[this.hash(key)]; for (const [k, v] of bkt) { if (k === key) return v; } return -1; }
remove(key: number): void { const bkt = this.table[this.hash(key)]; for (let i = 0; i < bkt.length; i++) { if (bkt[i][0] === key) { bkt.splice(i, 1); return; } } }}Editorial#
Separate chaining: collisions stay in the bucket list, lookups scan the chain. With B = 1024 and up to 10^4 keys uniformly distributed, average chain length is ~10 — operations are effectively O(1). Doubling-on-load would be needed for production, but the problem’s cap makes it unnecessary.
Complexity#
- Time: O(1) average, O(N / B) worst per op.
- Space: O(N + B).
Concept revision#
Related#