Coding Interview PatternsInsert Delete GetRandom O(1) - Duplicates Allowed
HardCustom Data Structures
Insert Delete GetRandom O(1) - Duplicates Allowed
Explanation & Solution
Description
Design a data structure that supports inserting and removing integers (including duplicates) and returning a random element, all in O(1) average time. Each element must have equal probability of being returned by getRandom.
Implement a function that processes an array of operations and an array of arguments:
"RandomizedCollection"— Initializes the collection."insert"— Insertsvalinto the collection. Returnstrueifvalwas not already present,falseif it was."remove"— Removes one occurrence ofvalfrom the collection. Returnstrueifvalwas present,falseotherwise."getRandom"— Returns a random element. Each element (including duplicates) must have equal probability.
Input:operations = ["RandomizedCollection","insert","insert","insert","remove","getRandom"], args = [[],[1],[1],[2],[1],[]]
0
RandomizedCollection
1
insert
2
insert
3
insert
4
remove
5
getRandom
Output:[null,true,false,true,true,1]
0
null
1
true
2
false
3
true
4
true
5
1
Explanation: Insert 1 (new→true), insert 1 again (dup→false), insert 2 (new→true). Remove one 1 (→true). Collection is [1,2]. getRandom must return 1 (only one left if 2 wasn't the answer — here collection has equal 1s and 2s; getRandom=1 in this deterministic test).
Constraints
-2^31 <= val <= 2^31 - 1- At most
2 * 10^5calls toinsert,remove, andgetRandom - There will be at least one element when
getRandomis called
Approach
Custom Data Structures pattern
1. Array + Map of Index Sets
- list: stores all values (including duplicates) for O(1) random access
- valToIdx: maps
value → Set<indices>— all positions where that value appears inlist
2. Implement Insert
- Track whether the value existed before (for the return value)
- Add the new index (
list.length) tovalToIdx[val] - Append
valtolist
3. Implement Remove — The Swap Trick
- Pick any index of
valfromvalToIdx[val](use the first one) - Swap
list[removeIdx]withlist[lastIdx](the last element) - Update
valToIdx[lastVal]: removelastIdx, addremoveIdx - Pop the last element from
list - Remove
removeIdxfromvalToIdx[val]
4. Handle the Edge Case: Removing the Last Element
- If
removeIdx === lastIdx, the value to remove IS the last element - Skip the swap step — just pop and remove the index
5. GetRandom
- Pick a random index from
listusingMath.random() - Since
listholds all occurrences, duplicates have proportional probability automatically
Key Insight
- The set of indices per value (instead of a single index as in the non-duplicate version) handles multiple occurrences
- The swap-and-pop trick maintains O(1) removal without shifting elements
Time
O(1) average for all operationsSpace
O(n) where n is total insertions