380. Insert Delete Get Random O(1) - Explanation
Description
Implement the RandomizedSet class:
RandomizedSet()Initializes theRandomizedSetobject.bool insert(int val)Inserts an itemvalinto the set if not present. Returnstrueif the item was not present,falseotherwise.bool remove(int val)Removes an itemvalfrom the set if present. Returnstrueif the item was present,falseotherwise.int getRandom()Returns a random element from the current set of elements (it's guaranteed that at least one element exists when this method is called). Each element must have the same probability of being returned.
You must implement the functions of the class such that each function works in average O(1) time complexity.
Example 1:
Input: ["RandomizedSet", "insert", "remove", "insert", "getRandom", "remove", "insert", "getRandom"]
[[], [1], [2], [2], [], [1], [2], []]
Output: [null, true, false, true, 2, true, false, 2]Explanation:
RandomizedSet randomizedSet = new RandomizedSet();
randomizedSet.insert(1); // Inserts 1 to the set. Returns true as 1 was inserted successfully.
randomizedSet.remove(2); // Returns false as 2 does not exist in the set.
randomizedSet.insert(2); // Inserts 2 to the set, returns true. Set now contains [1,2].
randomizedSet.getRandom(); // getRandom() should return either 1 or 2 randomly.
randomizedSet.remove(1); // Removes 1 from the set, returns true. Set now contains [2].
randomizedSet.insert(2); // 2 was already in the set, so return false.
randomizedSet.getRandom(); // Since 2 is the only number in the set, getRandom() will always return 2.
Constraints:
-((2^31)-1) <= val <= ((2^31)-1)- At most
2,00,000calls will be made toinsert,remove, andgetRandom. - There will be at least one element in the data structure when
getRandomis called.
Topics
Prerequisites
Before attempting this problem, you should be comfortable with:
- Hash Maps - Understanding O(1) average-case insert, delete, and lookup operations
- Dynamic Arrays - Understanding how arrays support O(1) random access and O(1) amortized append/pop at the end
- Swap-and-Pop Technique - Knowing how to achieve O(1) deletion from an unordered collection by swapping with the last element
1. Hash Map
Intuition
A hash map provides O(1) average time for insert and delete operations, making it a natural choice for the first two requirements. However, hash maps don't support random access by index.
For getRandom(), we need to pick a random element, but iterating to a random position takes O(n) time. We convert the keys to a list and pick a random index.
This approach sacrifices getRandom() performance to keep the implementation simple.
Algorithm
- Initialize a hash map
numMapand a size counter. - insert(val): If
valexists in the map, returnfalse. Otherwise, add it to the map with any value and increment size. Returntrue. - remove(val): If
valdoesn't exist in the map, returnfalse. Otherwise, delete it from the map and decrement size. Returntrue. - getRandom(): Generate a random index from
0tosize - 1. Convert the map's keys to a list and return the element at that index.
class RandomizedSet:
def __init__(self):
self.numMap = {}
self.size = 0
def insert(self, val: int) -> bool:
if val in self.numMap:
return False
self.numMap[val] = 1
self.size += 1
return True
def remove(self, val: int) -> bool:
if val not in self.numMap:
return False
del self.numMap[val]
self.size -= 1
return True
def getRandom(self) -> int:
idx = random.randint(0, self.size - 1)
return list(self.numMap.keys())[idx]Time & Space Complexity
- Time complexity: for , for other function calls.
- Space complexity:
2. Hash Map + List
Intuition
To achieve O(1) for all operations including getRandom(), we combine a hash map with a dynamic array. The array stores the actual values and allows random access, while the hash map stores each value's index in the array for fast lookups.
The tricky part is deletion: removing from the middle of an array is O(n). We solve this by swapping the element to delete with the last element, then removing from the end in O(1) time.
This swap-and-pop technique is a common pattern for O(1) deletion from unordered collections.
Algorithm
- Initialize a hash map
numMap(value to index) and a listnums. - insert(val): If
valexists in the map, returnfalse. Otherwise, addvalto the end of the list and store its index in the map. Returntrue. - remove(val): If
valdoesn't exist, returnfalse. Get the index ofval, swap it with the last element in the list, update the swapped element's index in the map, remove the last element from the list, and deletevalfrom the map. Returntrue. - getRandom(): Return a random element from the list using a random index.
class RandomizedSet:
def __init__(self):
self.numMap = {}
self.nums = []
def insert(self, val: int) -> bool:
if val in self.numMap:
return False
self.numMap[val] = len(self.nums)
self.nums.append(val)
return True
def remove(self, val: int) -> bool:
if val not in self.numMap:
return False
idx = self.numMap[val]
last = self.nums[-1]
self.nums[idx] = last
self.numMap[last] = idx
self.nums.pop()
del self.numMap[val]
return True
def getRandom(self) -> int:
return random.choice(self.nums)Time & Space Complexity
- Time complexity: for each function call.
- Space complexity:
Common Pitfalls
Not Updating the Swapped Element's Index
During removal, when swapping the element to delete with the last element, a critical step is updating the last element's index in the hash map before removing anything. Forgetting numMap[last] = idx causes the hash map to have a stale index for the swapped element, leading to incorrect behavior on subsequent operations involving that value.
Edge Case When Removing the Last Element
When the element to remove is already at the last position (i.e., idx == len(nums) - 1), the swap operation effectively swaps the element with itself. While this works correctly in most implementations, some code paths may have issues if they delete from the map before updating it. The order of operations matters: update the map first, then remove from both the list and map.
Using Hash Map Only Without Array
A common initial approach is to use only a hash map, which provides O(1) insert and delete but O(n) for getRandom() since hash maps do not support random index access. Converting keys to a list on each getRandom() call defeats the O(1) requirement. The combination of hash map plus array is essential, where the array enables O(1) random access and the hash map enables O(1) lookup for the swap-and-pop deletion technique.
Sign in to join the discussion