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 <= 1,000,00010,000 calls will be made to put, get, and remove.Before attempting this problem, you should be comfortable with:
Since keys are constrained to the range [0, 1000000], we can use direct addressing. We allocate an array where the index represents the key and the value at that index is the stored value. We use -1 to indicate that a key is not present. This gives O(1) time for all operations at the cost of fixed memory usage regardless of how many keys are actually stored.
1000001 with all values set to -1.put(key, value): Set map[key] = value.get(key): Return map[key] (returns -1 if the key was never set or was removed).remove(key): Set map[key] = -1.To reduce memory usage, we use a hash table with separate chaining. We create an array of buckets (smaller than the key range) and use a hash function (key modulo bucket count) to determine which bucket a key belongs to. Each bucket is a linked list that stores key-value pairs. This handles collisions by chaining multiple entries in the same bucket.
1000 buckets, each containing a dummy head node for a linked list.hash(key) as key % 1000.put(key, value): Traverse the linked list at hash(key). If a node with the matching key exists, update its value. Otherwise, append a new node with the key-value pair.get(key): Traverse the linked list at hash(key). If a node with the matching key is found, return its value. Otherwise, return -1.remove(key): Traverse the linked list at hash(key). If a node with the matching key is found, remove it by updating the previous node's next pointer.class ListNode:
def __init__(self, key = -1, val = -1, next = None):
self.key = key
self.val = val
self.next = next
class MyHashMap:
def __init__(self):
self.map = [ListNode() for _ in range(1000)]
def hash(self, key: int) -> int:
return key % len(self.map)
def put(self, key: int, value: int) -> None:
cur = self.map[self.hash(key)]
while cur.next:
if cur.next.key == key:
cur.next.val = value
return
cur = cur.next
cur.next = ListNode(key, value)
def get(self, key: int) -> int:
cur = self.map[self.hash(key)].next
while cur:
if cur.key == key:
return cur.val
cur = cur.next
return -1
def remove(self, key: int) -> None:
cur = self.map[self.hash(key)]
while cur.next:
if cur.next.key == key:
cur.next = cur.next.next
return
cur = cur.nextWhere is the number of keys, is the size of the map () and is the number of unique keys.
When a key already exists, put() should update its value rather than adding a duplicate entry.
# Wrong - creates duplicate entries
def put(self, key: int, value: int) -> None:
cur = self.map[self.hash(key)]
while cur.next:
cur = cur.next
cur.next = ListNode(key, value) # Always adds new node!
# Correct - update if exists, otherwise add
def put(self, key: int, value: int) -> None:
cur = self.map[self.hash(key)]
while cur.next:
if cur.next.key == key:
cur.next.val = value # Update existing
return
cur = cur.next
cur.next = ListNode(key, value) # Add newWhen getting a value, start from dummy.next (actual first element), not the dummy head itself.
# Wrong - checks dummy node's key
def get(self, key: int) -> int:
cur = self.map[self.hash(key)] # Starts at dummy
while cur:
if cur.key == key: # Dummy has key=-1, might match!
return cur.val
cur = cur.next
return -1
# Correct - skip dummy node
def get(self, key: int) -> int:
cur = self.map[self.hash(key)].next # Start after dummy
while cur:
if cur.key == key:
return cur.val
cur = cur.next
return -1