Design a HashMap without using any built-in hash table libraries.
Implement the MyHashMap
class:
MyHashMap()
initializes the object with an empty map.void put(int key, int value)
inserts a (key, value)
pair into the HashMap. If the key
already exists in the map, update the corresponding value
.int get(int key)
returns the value
to which the specified key
is mapped, or -1
if this map contains no mapping for the key
.void remove(key)
removes the key
and its corresponding value
if the map contains the mapping for the key
.Example 1:
Input ["MyHashMap", "put", "put", "get", "get", "put", "get", "remove", "get"] [[], [1, 1], [2, 2], [1], [3], [2, 1], [2], [2], [2]] Output [null, null, null, 1, -1, null, 1, null, -1] Explanation MyHashMap myHashMap = new MyHashMap(); myHashMap.put(1, 1); // The map is now [[1,1]] myHashMap.put(2, 2); // The map is now [[1,1], [2,2]] myHashMap.get(1); // return 1, The map is now [[1,1], [2,2]] myHashMap.get(3); // return -1 (i.e., not found), The map is now [[1,1], [2,2]] myHashMap.put(2, 1); // The map is now [[1,1], [2,1]] (i.e., update the existing value) myHashMap.get(2); // return 1, The map is now [[1,1], [2,1]] myHashMap.remove(2); // remove the mapping for 2, The map is now [[1,1]] myHashMap.get(2); // return -1 (i.e., not found), The map is now [[1,1]]
Constraints:
0 <= key, value <= 106
104
calls will be made to put
, get
, and remove
.When you get asked this question in a real-life environment, it will often be ambiguous (especially at FAANG). Make sure to ask these questions in that case:
Imagine we're building a very basic phone book. The brute force way to manage this book is to keep a simple list where we store each name and number as it comes. When we need to find a number, we simply go through the entire list to see if the name exists.
Here's how the algorithm would work step-by-step:
class MyHashMap:
def __init__(self):
self.phone_book = []
def put(self, key: int, value: int) -> None:
# Add a new entry to the end of the list.
self.phone_book.append((key, value))
def get(self, key: int) -> int:
# Iterate through the list to find the key.
for entry_key, entry_value in self.phone_book:
if entry_key == key:
return entry_value
return -1
def remove(self, key: int) -> None:
# Find the key and remove the entry.
for index, (entry_key, entry_value) in enumerate(self.phone_book):
if entry_key == key:
# Avoid index issues by deleting the element.
del self.phone_book[index]
return
We're creating a tool that stores information using keys, like a real-world dictionary. Instead of looking through every possible storage location, we'll use a clever system to quickly find the right spot for each key. This system will allow us to store, retrieve, and update information efficiently.
Here's how the algorithm would work step-by-step:
class MyHashMap:
def __init__(self):
self.key_space = 2069
self.hash_table = [[] for _ in range(self.key_space)]
def put(self, key: int, value: int) -> None:
key_index = key % self.key_space
bucket = self.hash_table[key_index]
for i, (existing_key, existing_value) in enumerate(bucket):
if existing_key == key:
bucket[i] = (key, value)
return
bucket.append((key, value))
def get(self, key: int) -> int:
key_index = key % self.key_space
bucket = self.hash_table[key_index]
for existing_key, existing_value in bucket:
if existing_key == key:
return existing_value
return -1
def remove(self, key: int) -> None:
key_index = key % self.key_space
bucket = self.hash_table[key_index]
# Need to iterate with index to remove the element
for i, (existing_key, existing_value) in enumerate(bucket):
if existing_key == key:
# Removing the key if it exists.
del bucket[i]
return
# Your MyHashMap object will be instantiated and called as such:
# my_hash_map = MyHashMap()
# my_hash_map.put(key, value)
# param_2 = my_hash_map.get(key)
# my_hash_map.remove(key)
Case | How to Handle |
---|---|
Null or negative keys | Define the HashMap's behavior with null/negative keys, either by throwing an exception or treating them as valid keys by appropriate hashing. |
Zero value | The zero value should be handled the same way as any other valid integer value; no special treatment is required. |
Extremely large number of keys (close to Integer.MAX_VALUE) | Ensure the initial capacity and load factor of the HashMap are appropriately set to handle a large number of keys and minimize collisions to maintain reasonable performance. |
Hash collisions (multiple keys map to the same index) | Separate chaining should handle collisions gracefully by storing multiple key-value pairs at the same index in a linked list or other suitable data structure. |
Deleting a key that doesn't exist | The remove operation should not throw an error if the key doesn't exist; instead, it should simply do nothing, ensuring stability of the HashMap. |
Updating value of existing key to null | The 'put' operation should handle updating a key's value to null correctly, overwriting any previous non-null value. |
Very uneven key distribution leading to excessive collisions in some buckets | Consider using a better hash function or dynamically resizing the hash table to redistribute keys and reduce the length of collision chains. |
Integer overflow when calculating hash | Use a modulo operator to ensure the hash index stays within the valid range of the underlying array, or choose a hash function that minimizes overflow risk. |