RandomizedCollection
is a data structure that contains a collection of numbers, possibly duplicates (i.e., a multiset). It should support inserting and removing specific elements and also reporting a random element.
Implement the RandomizedCollection
class:
RandomizedCollection()
Initializes the empty RandomizedCollection
object.bool insert(int val)
Inserts an item val
into the multiset, even if the item is already present. Returns true
if the item is not present, false
otherwise.bool remove(int val)
Removes an item val
from the multiset if present. Returns true
if the item is present, false
otherwise. Note that if val
has multiple occurrences in the multiset, we only remove one of them.int getRandom()
Returns a random element from the current multiset of elements. The probability of each element being returned is linearly related to the number of the same values the multiset contains.You must implement the functions of the class such that each function works on average O(1)
time complexity.
Note: The test cases are generated such that getRandom
will only be called if there is at least one item in the RandomizedCollection
.
Example 1:
Input ["RandomizedCollection", "insert", "insert", "insert", "getRandom", "remove", "getRandom"] [[], [1], [1], [2], [], [1], []] Output [null, true, false, true, 2, true, 1] Explanation RandomizedCollection randomizedCollection = new RandomizedCollection(); randomizedCollection.insert(1); // return true since the collection does not contain 1. // Inserts 1 into the collection. randomizedCollection.insert(1); // return false since the collection contains 1. // Inserts another 1 into the collection. Collection now contains [1,1]. randomizedCollection.insert(2); // return true since the collection does not contain 2. // Inserts 2 into the collection. Collection now contains [1,1,2]. randomizedCollection.getRandom(); // getRandom should: // - return 1 with probability 2/3, or // - return 2 with probability 1/3. randomizedCollection.remove(1); // return true since the collection contains 1. // Removes 1 from the collection. Collection now contains [1,2]. randomizedCollection.getRandom(); // getRandom should return 1 or 2, both equally likely.
Constraints:
-231 <= val <= 231 - 1
2 * 105
calls in total will be made to insert
, remove
, and getRandom
.getRandom
is called.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:
The brute force method for this problem involves directly mimicking the operations of insert, delete, and getRandom. We will use a simple list to store the numbers, and for each operation, perform the most straightforward action possible.
Here's how the algorithm would work step-by-step:
import random
class RandomizedCollection:
def __init__(self):
self.number_list = []
def insert(self, value):
self.number_list.append(value)
return True
def remove(self, value):
# We need to iterate and remove the FIRST occurence only
for index, number in enumerate(self.number_list):
if number == value:
del self.number_list[index]
# We only delete the first one
return True
return False
def getRandom(self):
# Pick a random element from the list
random_index = random.randint(0, len(self.number_list) - 1)
return self.number_list[random_index]
The key is to use two data structures working together. One remembers the exact position of each number, and the other lets us quickly swap and remove numbers when needed, even with duplicates.
Here's how the algorithm would work step-by-step:
import random
class RandomizedCollection:
def __init__(self):
self.number_list = []
self.value_to_indices = {}
def insert(self, value):
self.number_list.append(value)
if value not in self.value_to_indices:
self.value_to_indices[value] = set()
self.value_to_indices[value].add(len(self.number_list) - 1)
return len(self.value_to_indices[value]) == 1
def remove(self, value):
if value not in self.value_to_indices or not self.value_to_indices[value]:
return False
# Get an index of the value to remove.
index_to_remove = self.value_to_indices[value].pop()
# If the element to remove is not the last element
if index_to_remove < len(self.number_list) - 1:
last_element = self.number_list[-1]
# Move the last element to the position of the element to remove.
self.number_list[index_to_remove] = last_element
# Update index of the moved last element
self.value_to_indices[last_element].add(index_to_remove)
# Remove index of the moved last element
self.value_to_indices[last_element].remove(len(self.number_list) - 1)
# Remove the last element from the list and update the indices.
self.number_list.pop()
# Clean up if the value is no longer present
if not self.value_to_indices[value]:
del self.value_to_indices[value]
return True
def getRandom(self):
# Get a random element from the list.
return random.choice(self.number_list)
Case | How to Handle |
---|---|
Inserting null or invalid value (e.g., non-integer) | Raise an exception or return an error code to indicate invalid input. |
Inserting a very large number of duplicate values | The HashMap/ArrayList combination should scale linearly with the number of inserted elements, however very large numbers may cause memory constraints. |
Removing a value that doesn't exist in the collection | Return false or do nothing if the value is not present. |
getRandom() called on an empty collection after multiple insertions and removals | Throw an exception, return null, or some other appropriate error handling mechanism, as the prompt stated this condition would not exist; this tests for correct remove logic. |
Removing the last occurrence of a specific value | Ensure that both the list and the hashmap are updated correctly when the last instance is removed. |
Integer overflow in the value being inserted or removed | Use appropriate data types (e.g., long) or validate input to prevent overflows; alternatively consider using a language with built-in overflow protection. |
Extreme boundary values (Integer.MAX_VALUE, Integer.MIN_VALUE) | The data structures should handle these values correctly without causing unexpected behavior. |
Negative numbers are inserted | The data structures should be able to store and retrieve negative numbers correctly. |