Design HashMap
Problem statement
Build your own hash map class, MyHashMap, without using any built-in hash table library. Keys and values are non-negative integers up to 1,000,000. It must support:
put(key, value): store the value for the key, replacing any existing value.get(key): return the stored value, or-1if the key is not present.remove(key): delete the key if it is present.
Picture a small table from port number to the process ID listening on it.
Examples
Example 1
Input: put(8080, 1), put(443, 2), get(8080), get(22), put(443, 5), get(443), remove(8080), get(8080)
Output: [1, -1, 5, -1]
Explanation: Only the get results are shown. Port 443 is overwritten with 5, and 8080 returns -1 after it is removed.
Example 2
Input: remove(7), put(0, 0), get(0), get(7)
Output: [0, -1]
Explanation: Removing a missing key does nothing. Key 0 with value 0 is a valid entry.
Hints
Approach
This is how a real hash map works: an array of b buckets, where each key goes into bucket key % b, and each bucket holds a short list of pairs (called separate chaining).
- Create
bempty buckets. A prime such as 1009 spreads keys evenly. - To find a key's bucket, compute
key % b. put,getandremovedo the same linear scan as before, but only inside that one bucket.
With n keys spread over b buckets, each bucket holds about n / b pairs, so operations are O(1) on average. The worst case is still O(n) if every key lands in the same bucket.
O(1) average per operationSpace O(n + b)class MyHashMap: SIZE = 1009 def __init__(self): self.buckets = [[] for _ in range(self.SIZE)] def _bucket(self, key: int) -> list: return self.buckets[key % self.SIZE] def put(self, key: int, value: int) -> None: bucket = self._bucket(key) for pair in bucket: if pair[0] == key: pair[1] = value return bucket.append([key, value]) def get(self, key: int) -> int: for k, v in self._bucket(key): if k == key: return v return -1 def remove(self, key: int) -> None: bucket = self._bucket(key) for i, (k, _) in enumerate(bucket): if k == key: bucket.pop(i) returnFollow-up questions
- Add automatic resizing when the load factor goes above 0.75.
- Support string keys. How would you hash them?
Frequently asked questions
That also works here, with O(1) worst-case operations, because keys are small non-negative integers. It is called a direct-address table. It uses memory for every possible key, though, and it does not generalise to strings or large keys. Mention it, then show the bucket version.
Real hash maps track the load factor (entries divided by buckets). When it passes a threshold, often around 0.75, they allocate more buckets and move every entry to its new bucket. That keeps the average cost O(1).
Hash maps sit under caches, service registries and routing tables. Knowing about collisions, load factors and resizing helps when you reason about things like consistent hashing across cache nodes or why a lookup table got slow.