Insert Delete GetRandom O(1) - Duplicates allowed

hard array hash map design

Problem

Design RandomizedCollection with insert(val), remove(val), and getRandom() in average O(1). Duplicates are allowed, and getRandom must produce each value with probability proportional to its multiplicity.

Inputinsert(1); insert(1); insert(2); getRandom(); remove(1)
Output1 / 1 / 2 (mix) → array shrinks
Keep values in an array; a map of value → set of indices makes remove "swap with last & pop" still O(1).

import random
from collections import defaultdict

class RandomizedCollection:
    def __init__(self):
        self.arr = []
        self.idx = defaultdict(set)

    def insert(self, val):
        self.idx[val].add(len(self.arr))
        self.arr.append(val)
        return len(self.idx[val]) == 1

    def remove(self, val):
        if not self.idx[val]:
            return False
        i = next(iter(self.idx[val]))
        last = self.arr[-1]
        self.arr[i] = last
        self.idx[val].discard(i)
        self.idx[last].discard(len(self.arr) - 1)
        if i < len(self.arr) - 1:
            self.idx[last].add(i)
        self.arr.pop()
        return True

    def getRandom(self):
        return random.choice(self.arr)
class RandomizedCollection {
  constructor() { this.arr = []; this.idx = new Map(); }
  _set(v) { if (!this.idx.has(v)) this.idx.set(v, new Set()); return this.idx.get(v); }
  insert(val) {
    const s = this._set(val);
    s.add(this.arr.length);
    this.arr.push(val);
    return s.size === 1;
  }
  remove(val) {
    const s = this.idx.get(val);
    if (!s || s.size === 0) return false;
    const i = s.values().next().value;
    const last = this.arr[this.arr.length - 1];
    this.arr[i] = last;
    s.delete(i);
    this._set(last).delete(this.arr.length - 1);
    if (i < this.arr.length - 1) this._set(last).add(i);
    this.arr.pop();
    return true;
  }
  getRandom() { return this.arr[Math.floor(Math.random() * this.arr.length)]; }
}
class RandomizedCollection {
    List<Integer> arr = new ArrayList<>();
    Map<Integer, Set<Integer>> idx = new HashMap<>();
    Random rng = new Random();
    public boolean insert(int val) {
        idx.computeIfAbsent(val, k -> new HashSet<>()).add(arr.size());
        arr.add(val);
        return idx.get(val).size() == 1;
    }
    public boolean remove(int val) {
        Set<Integer> s = idx.get(val);
        if (s == null || s.isEmpty()) return false;
        int i = s.iterator().next();
        int last = arr.get(arr.size() - 1);
        arr.set(i, last);
        s.remove(i);
        idx.get(last).remove(arr.size() - 1);
        if (i < arr.size() - 1) idx.get(last).add(i);
        arr.remove(arr.size() - 1);
        return true;
    }
    public int getRandom() { return arr.get(rng.nextInt(arr.size())); }
}
class RandomizedCollection {
    vector<int> arr;
    unordered_map<int, unordered_set<int>> idx;
public:
    bool insert(int val) {
        idx[val].insert((int)arr.size());
        arr.push_back(val);
        return idx[val].size() == 1;
    }
    bool remove(int val) {
        if (!idx.count(val) || idx[val].empty()) return false;
        int i = *idx[val].begin();
        int last = arr.back();
        arr[i] = last;
        idx[val].erase(i);
        idx[last].erase((int)arr.size() - 1);
        if (i < (int)arr.size() - 1) idx[last].insert(i);
        arr.pop_back();
        return true;
    }
    int getRandom() { return arr[rand() % arr.size()]; }
};
Time: O(1) average per op Space: O(n)