
Problem Statement
The RandomizedCollection
is a versatile data structure designed to manage a collection of integers, where duplicates are allowed. This multiset supports multiple operations: adding new values (with duplicates permitted), removing existing values, and randomly selecting a value from the set. The design must ensure that each operation—specifically insert
, remove
, and getRandom
—operates with an average time complexity of O(1).
insert(int val)
: Inserts a value into the collection. Returnstrue
if the value was not already present; otherwise, returnsfalse
.remove(int val)
: Removes one occurrence of a value from the collection. Returnstrue
if the value existed; otherwise, returnsfalse
.getRandom()
: Returns a random element from the collection. Each element's probability of being returned is proportional to its frequency.
Examples
Example 1
Input:
["RandomizedCollection", "insert", "insert", "insert", "getRandom", "remove", "getRandom"] [[], [1], [1], [2], [], [1], []]
Output:
[null, true, false, true, 2, true, 1]
Explanation:
RandomizedCollection collection = new RandomizedCollection(); collection.insert(1); // Returns true, inserted first 1 collection.insert(1); // Returns false, 1 already present collection.insert(2); // Returns true, inserted 2 collection.getRandom(); // Returns 1 (2/3 chance) or 2 (1/3 chance) collection.remove(1); // Returns true, removes one 1 collection.getRandom(); // Returns 1 or 2, each has 50% chance
Constraints
-2^31 <= val <= 2^31 - 1
- At most
2 * 10^5
total calls will be made toinsert
,remove
, andgetRandom
- There will be at least one element in the collection when
getRandom
is called
Approach and Intuition
Data Structure Design:
- Use a list to store all elements.
- Use a hash map to store a set of indices for each value.
Insert Operation:
- Append the value to the list.
- Add the new index to the map under that value.
- Return
true
if it’s the first insertion of that value, otherwisefalse
.
Remove Operation:
- If the value is not present in the map, return
false
. - Get any index of the value from the map.
- Swap this index with the last element in the list (if it’s not already the last).
- Update the map entries for the swapped value.
- Remove the last element from the list.
- If no indices remain for the removed value, delete it from the map.
- Return
true
.
- If the value is not present in the map, return
getRandom Operation:
- Choose a random index in the list using
randrange
. - Return the element at that index.
- Choose a random index in the list using
By combining a list for fast random access and a dictionary for efficient index tracking and updating, the structure ensures O(1) average time complexity for all required operations. This design supports efficient multiset behavior with randomization.
Solutions
- Java
public class RandomizedSet {
ArrayList<Integer> elements;
HashMap<Integer, Set<Integer>> indexes;
java.util.Random random = new java.util.Random();
public RandomizedSet() {
elements = new ArrayList<Integer>();
indexes = new HashMap<Integer, Set<Integer>>();
}
public boolean insert(int value) {
if (!indexes.containsKey(value)) indexes.put(value, new LinkedHashSet<Integer>());
indexes.get(value).add(elements.size());
elements.add(value);
return indexes.get(value).size() == 1;
}
public boolean remove(int value) {
if (!indexes.containsKey(value) || indexes.get(value).isEmpty()) return false;
int removeIndex = indexes.get(value).iterator().next();
indexes.get(value).remove(removeIndex);
int lastElement = elements.get(elements.size() - 1);
elements.set(removeIndex, lastElement);
indexes.get(lastElement).add(removeIndex);
indexes.get(lastElement).remove(elements.size() - 1);
elements.remove(elements.size() - 1);
return true;
}
public int getRandom() {
return elements.get(random.nextInt(elements.size()));
}
}
This article presents an efficient solution for enabling the operations: insert, delete, and getRandom on a data structure, allowing duplicates, in constant time O(1). Written in Java, the implementation utilizes a combination of an ArrayList
for storing elements and a HashMap
where each key is an integer and the associated value is a Set
of integers that track the indices of the elements in the ArrayList.
Here's how the operations function:
Insert Operation:
- Check if the value is not already present in the
HashMap
. - If not present, initialize a new
LinkedHashSet
for that value in theHashMap
. - Add the index of the new value to the
Set
associated with the value in theHashMap
. - Append the value to the
ArrayList
. - Return true if it's the first insertion of the value, otherwise, return false.
- Check if the value is not already present in the
Delete Operation:
- Verify presence of the value in the
HashMap
and ensure theSet
is not empty. - Retrieve the index of one occurrence of the value using the
Set
iterator. - Remove the index from the
Set
. - Swap the last element of the
ArrayList
with the element at the target index to maintain array list continuity. - Update the
HashMap
for the last element of theArrayList
that was swapped. - Remove the last element from the
ArrayList
. - Return true if removal was successful, false if not.
- Verify presence of the value in the
GetRandom Operation:
- Select and return a random element from the
ArrayList
using a built-in random number generator.
- Select and return a random element from the
This implementation handles the challenges of maintaining index integrity during deletions and ensuring random selection is unbiased despite duplicates, all while operating in constant time for all functionalities. This makes the solution both efficient and robust for real-world applications where such operations are critical.
No comments yet.