Design HashMap
Implement put, get, and remove for integer keys without using a built-in hash table.
Intuition
A single list of key-value pairs is enough for correctness, but every operation may scan the entire collection. Hashing cuts that search down by sending a key to one bucket with key % bucket_count. Different keys can land in the same bucket, so a bucket cannot store just one value; it must keep a small chain of pairs and compare the original keys. Using a prime number of buckets helps ordinary integer keys spread out, while explicit collision handling preserves correctness regardless of the distribution.
When a problem asks you to implement a map rather than merely use one, split the design into hashing and collision resolution. The hash chooses a small search area; the bucket must still retain original keys so collisions do not overwrite unrelated mappings.
Approach
Hash each key to one collision bucket
Create a fixed array of buckets and choose key % bucket_count as the bucket index. Each bucket stores [key, value] pairs, not values alone, because two different keys may have the same remainder. The hash narrows the search; the stored key resolves the collision.
Update an existing pair before inserting
For put, scan only the selected bucket. If its key is already present, replace that pair's value and return; otherwise append a new pair. This keeps exactly one mapping per key, which makes later get and remove unambiguous.
Keep missing-key behaviour explicit
For get, return the matching value or -1 after the bucket is exhausted. For remove, delete the matching pair if it exists and otherwise do nothing. Average operations are constant time when keys are distributed, while a worst-case bucket containing every key still takes O(n).
Solution
Common pitfalls
Overwriting an entire collision bucket
self.buckets[key % self.bucket_count] = [key, value]
bucket.append([key, value])
Replacing the bucket destroys every different key that produced the same hash index.
Appending a duplicate mapping
bucket.append([key, value])
pair[1] = value
When the key already exists, put must update it. Appending leaves two conflicting values for one key.
Returning the bucket position
return index
return stored_value
The public result is the value associated with the key, not where the pair happens to sit inside its collision bucket.
Edge cases
put twice with the same keyThe second call finds the existing pair and replaces its value, so the bucket never contains duplicate mappings.
Both pairs remain in that bucket and each lookup compares full keys before returning a value.
The bucket scan finishes without deleting anything, matching the required no-op behaviour.