algoadvance

Design a HashMap without using any built-in hash table libraries. Implement the following operations:

Example:

MyHashMap hashMap = new MyHashMap();
hashMap.put(1, 1);         
hashMap.put(2, 2);         
hashMap.get(1);            // returns 1
hashMap.get(3);            // returns -1 (not found)
hashMap.put(2, 1);         // update the existing value
hashMap.get(2);            // returns 1 
hashMap.remove(2);         // remove the mapping for 2
hashMap.get(2);            // returns -1 (not found) 

Constraints:

Clarifying Questions

  1. Can I assume that all keys and values are non-negative integers?
  2. Is there any particular constraint on the size of the underlying data structure?

Strategy

  1. Data Structure:
    • Use a fixed-size array (bucket array) as the primary container to store the key-value pairs.
    • Each bucket will itself be a list to handle collisions using chaining.
  2. Hash Function:
    • Use a simple modulo operation to assign keys to buckets.
  3. Operations:
    • Put: Compute the hash index of the key, then iterate through the list in that bucket to check if the key exists. If it does, update the value. If not, append a new (key, value) pair.
    • Get: Compute the hash index of the key, then iterate through the list in that bucket to find the key. Return the value if found, otherwise return -1.
    • Remove: Compute the hash index of the key, then iterate through the list in that bucket to find the key. Remove the key-value pair if found.

Code

class MyHashMap:

    def __init__(self):
        # Define the number of buckets
        self.size = 1000
        self.buckets = [[] for _ in range(self.size)]

    def _hash(self, key: int) -> int:
        return key % self.size

    def put(self, key: int, value: int) -> None:
        hash_key = self._hash(key)
        for idx, (k, v) in enumerate(self.buckets[hash_key]):
            if k == key:
                self.buckets[hash_key][idx] = (key, value)
                return
        self.buckets[hash_key].append((key, value))

    def get(self, key: int) -> int:
        hash_key = self._hash(key)
        for k, v in self.buckets[hash_key]:
            if k == key:
                return v
        return -1

    def remove(self, key: int) -> None:
        hash_key = self._hash(key)
        bucket = self.buckets[hash_key]
        for idx, (k, v) in enumerate(bucket):
            if k == key:
                del bucket[idx]
                return

Time Complexity

This design leverages an array of linked lists (or vectors) and provides average constant-time complexity for basic operations, assuming a good hash function and a large enough array.

Try our interview co-pilot at AlgoAdvance.com