🚀 Phases 1–4 are live — Days 1–13 + Day 17 are fully written. See the roadmap →

Insert Delete GetRandom O(1) medium

Description

Design a RandomizedSet class that supports all three of these in O(1) average time:

  • insert(int val) — adds val to the set if not present. Returns true if added, false if it was already there.
  • remove(int val) — removes val from the set if present. Returns true if removed, false if not.
  • getRandom() — returns a uniformly random element from the current set. Each element must have equal probability.

Examples

> Case 1:
    set = RandomizedSet()
    set.insert(1)      → true   (1 wasn't there)
    set.remove(2)      → false  (2 wasn't there)
    set.insert(2)      → true
    set.getRandom()    → 1 or 2 with equal probability
    set.remove(1)      → true
    set.insert(2)      → false  (2 already exists)
    set.getRandom()    → 2 (only element left)

Constraints

  • -2^31 <= val <= 2^31 - 1
  • At most 2 * 10^5 calls.

Why neither a hash set nor an array alone works

StructureInsertRemoveGetRandom
Hash set aloneO(1)O(1)❌ no random access
Array aloneO(1) appendO(n) — find + shiftO(1) by index
Hash map + arrayO(1)O(1)O(1)

A hash set is fast for the membership checks but doesn’t support random access — you can’t pick the k-th element in O(1). An array gives you O(1) random access but slow remove if the element isn’t at the end.

The combined structure: an array holds the actual values (so getRandom is array[random_index]), and a hash map stores value → index_in_array for O(1) lookup.

The swap-and-pop trick for O(1) remove

How do we remove from the middle of an array in O(1)?

We don’t. Instead, when removing element x at index i:

  1. Look up i from the hash map.
  2. Copy the LAST element of the array to position i — overwriting x.
  3. Pop the last element off (O(1)).
  4. Update the hash map: the moved element’s new index is i. Delete x’s entry.

Two array writes + two hash map updates = O(1). The trade-off is the array no longer preserves insertion order — but for a set, order never mattered.

Code

⚠️

The order of operations in remove matters. If you del self.idx[val] before updating self.idx[last] = i, you’d be fine unless val == last (you’re removing the last element). Then del self.idx[val] first followed by idx[last] = i would re-create the entry — wrong. The shown order handles both cases correctly. Test on a single-element set to convince yourself.

Trace on insert(1), insert(2), insert(3), remove(2)

insert(1):  arr = [1],        idx = {1: 0}
insert(2):  arr = [1, 2],     idx = {1: 0, 2: 1}
insert(3):  arr = [1, 2, 3],  idx = {1: 0, 2: 1, 3: 2}

remove(2):
    i = idx[2] = 1
    last = arr[2] = 3
    arr[1] = 3      → arr = [1, 3, 3]
    idx[3] = 1      → idx = {1: 0, 2: 1, 3: 1}
    arr.pop()       → arr = [1, 3]
    del idx[2]      → idx = {1: 0, 3: 1}

The element 3 “swapped in” for 2 at index 1, and the array shrank to size 2. Both structures stay consistent.

Why getRandom is uniform

random.choice(arr) picks a uniformly random index, and the array contains exactly one copy of each set element. So each element is returned with probability 1/n. The swap-and-pop preserves this: every element is still at exactly one index, and indices 0..n-1 are all valid.

Variant: with duplicates (LeetCode 381)

If the set can contain duplicates (RandomizedCollection), the hash map must store a set of indices per value (since the same value can appear at multiple positions). On remove, pop any one of the indices for the chosen value. The swap-and-pop logic stays the same; just the bookkeeping for the moved element’s indices changes.

The bigger pattern: combining structures for combined invariants

LRU Cache and Insert-Delete-GetRandom are both flavors of the “two structures with cross-references” design pattern:

  • LRU = hash map + doubly linked list (key lookup + recency order).
  • Insert-Delete-GetRandom = hash map + dynamic array (key lookup + random access).
  • LFU = hash map + hash map of doubly linked lists (key lookup + frequency buckets).

Each problem demands invariants that no single structure satisfies, so you compose two — and use the hash map to keep them in sync.

Analysis

  • Time: O(1) average for all three operations.
  • Space: O(n) — both structures hold n entries.